]>
Commit | Line | Data |
---|---|---|
a0fa20a1 | 1 | // Copyright (c) 2009-2010 Satoshi Nakamoto |
f914f1a7 | 2 | // Copyright (c) 2009-2014 The Bitcoin Core developers |
fa94b9d5 | 3 | // Distributed under the MIT software license, see the accompanying |
a0fa20a1 | 4 | // file COPYING or http://www.opensource.org/licenses/mit-license.php. |
093303a8 | 5 | |
a0fa20a1 PW |
6 | #ifndef BITCOIN_COINS_H |
7 | #define BITCOIN_COINS_H | |
8 | ||
561e9e9d | 9 | #include "compressor.h" |
046392dc | 10 | #include "memusage.h" |
a0fa20a1 PW |
11 | #include "serialize.h" |
12 | #include "uint256.h" | |
13 | ||
14 | #include <assert.h> | |
15 | #include <stdint.h> | |
16 | ||
17 | #include <boost/foreach.hpp> | |
bc42503f | 18 | #include <boost/unordered_map.hpp> |
a0fa20a1 | 19 | |
fa94b9d5 MF |
20 | /** |
21 | * Pruned version of CTransaction: only retains metadata and unspent transaction outputs | |
a0fa20a1 PW |
22 | * |
23 | * Serialized format: | |
24 | * - VARINT(nVersion) | |
25 | * - VARINT(nCode) | |
26 | * - unspentness bitvector, for vout[2] and further; least significant byte first | |
27 | * - the non-spent CTxOuts (via CTxOutCompressor) | |
28 | * - VARINT(nHeight) | |
29 | * | |
30 | * The nCode value consists of: | |
31 | * - bit 1: IsCoinBase() | |
32 | * - bit 2: vout[0] is not spent | |
33 | * - bit 4: vout[1] is not spent | |
34 | * - The higher bits encode N, the number of non-zero bytes in the following bitvector. | |
35 | * - In case both bit 2 and bit 4 are unset, they encode N-1, as there must be at | |
36 | * least one non-spent output). | |
37 | * | |
38 | * Example: 0104835800816115944e077fe7c803cfa57f29b36bf87c1d358bb85e | |
39 | * <><><--------------------------------------------><----> | |
40 | * | \ | / | |
41 | * version code vout[1] height | |
42 | * | |
43 | * - version = 1 | |
44 | * - code = 4 (vout[1] is not spent, and 0 non-zero bytes of bitvector follow) | |
45 | * - unspentness bitvector: as 0 non-zero bytes follow, it has length 0 | |
46 | * - vout[1]: 835800816115944e077fe7c803cfa57f29b36bf87c1d35 | |
47 | * * 8358: compact amount representation for 60000000000 (600 BTC) | |
48 | * * 00: special txout type pay-to-pubkey-hash | |
49 | * * 816115944e077fe7c803cfa57f29b36bf87c1d35: address uint160 | |
50 | * - height = 203998 | |
51 | * | |
52 | * | |
53 | * Example: 0109044086ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4eebbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa486af3b | |
54 | * <><><--><--------------------------------------------------><----------------------------------------------><----> | |
55 | * / \ \ | | / | |
56 | * version code unspentness vout[4] vout[16] height | |
57 | * | |
58 | * - version = 1 | |
59 | * - code = 9 (coinbase, neither vout[0] or vout[1] are unspent, | |
60 | * 2 (1, +1 because both bit 2 and bit 4 are unset) non-zero bitvector bytes follow) | |
61 | * - unspentness bitvector: bits 2 (0x04) and 14 (0x4000) are set, so vout[2+2] and vout[14+2] are unspent | |
62 | * - vout[4]: 86ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4ee | |
63 | * * 86ef97d579: compact amount representation for 234925952 (2.35 BTC) | |
64 | * * 00: special txout type pay-to-pubkey-hash | |
65 | * * 61b01caab50f1b8e9c50a5057eb43c2d9563a4ee: address uint160 | |
66 | * - vout[16]: bbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa4 | |
67 | * * bbd123: compact amount representation for 110397 (0.001 BTC) | |
68 | * * 00: special txout type pay-to-pubkey-hash | |
69 | * * 8c988f1a4a4de2161e0f50aac7f17e7f9555caa4: address uint160 | |
70 | * - height = 120891 | |
71 | */ | |
72 | class CCoins | |
73 | { | |
74 | public: | |
fa94b9d5 | 75 | //! whether transaction is a coinbase |
a0fa20a1 PW |
76 | bool fCoinBase; |
77 | ||
fa94b9d5 | 78 | //! unspent transaction outputs; spent outputs are .IsNull(); spent outputs at the end of the array are dropped |
a0fa20a1 PW |
79 | std::vector<CTxOut> vout; |
80 | ||
fa94b9d5 | 81 | //! at which height this transaction was included in the active block chain |
a0fa20a1 PW |
82 | int nHeight; |
83 | ||
fa94b9d5 MF |
84 | //! version of the CTransaction; accesses to this value should probably check for nHeight as well, |
85 | //! as new tx version will probably only be introduced at certain heights | |
a0fa20a1 PW |
86 | int nVersion; |
87 | ||
f28aec01 PW |
88 | void FromTx(const CTransaction &tx, int nHeightIn) { |
89 | fCoinBase = tx.IsCoinBase(); | |
90 | vout = tx.vout; | |
91 | nHeight = nHeightIn; | |
92 | nVersion = tx.nVersion; | |
a0fa20a1 PW |
93 | ClearUnspendable(); |
94 | } | |
95 | ||
fa94b9d5 | 96 | //! construct a CCoins from a CTransaction, at a given height |
f28aec01 PW |
97 | CCoins(const CTransaction &tx, int nHeightIn) { |
98 | FromTx(tx, nHeightIn); | |
99 | } | |
100 | ||
101 | void Clear() { | |
102 | fCoinBase = false; | |
103 | std::vector<CTxOut>().swap(vout); | |
104 | nHeight = 0; | |
105 | nVersion = 0; | |
106 | } | |
107 | ||
fa94b9d5 | 108 | //! empty constructor |
a0fa20a1 PW |
109 | CCoins() : fCoinBase(false), vout(0), nHeight(0), nVersion(0) { } |
110 | ||
fa94b9d5 | 111 | //!remove spent outputs at the end of vout |
a0fa20a1 PW |
112 | void Cleanup() { |
113 | while (vout.size() > 0 && vout.back().IsNull()) | |
114 | vout.pop_back(); | |
115 | if (vout.empty()) | |
116 | std::vector<CTxOut>().swap(vout); | |
117 | } | |
118 | ||
119 | void ClearUnspendable() { | |
120 | BOOST_FOREACH(CTxOut &txout, vout) { | |
121 | if (txout.scriptPubKey.IsUnspendable()) | |
122 | txout.SetNull(); | |
123 | } | |
124 | Cleanup(); | |
125 | } | |
126 | ||
127 | void swap(CCoins &to) { | |
128 | std::swap(to.fCoinBase, fCoinBase); | |
129 | to.vout.swap(vout); | |
130 | std::swap(to.nHeight, nHeight); | |
131 | std::swap(to.nVersion, nVersion); | |
132 | } | |
133 | ||
fa94b9d5 | 134 | //! equality test |
a0fa20a1 PW |
135 | friend bool operator==(const CCoins &a, const CCoins &b) { |
136 | // Empty CCoins objects are always equal. | |
137 | if (a.IsPruned() && b.IsPruned()) | |
138 | return true; | |
139 | return a.fCoinBase == b.fCoinBase && | |
140 | a.nHeight == b.nHeight && | |
141 | a.nVersion == b.nVersion && | |
142 | a.vout == b.vout; | |
143 | } | |
144 | friend bool operator!=(const CCoins &a, const CCoins &b) { | |
145 | return !(a == b); | |
146 | } | |
147 | ||
148 | void CalcMaskSize(unsigned int &nBytes, unsigned int &nNonzeroBytes) const; | |
149 | ||
150 | bool IsCoinBase() const { | |
151 | return fCoinBase; | |
152 | } | |
153 | ||
154 | unsigned int GetSerializeSize(int nType, int nVersion) const { | |
155 | unsigned int nSize = 0; | |
156 | unsigned int nMaskSize = 0, nMaskCode = 0; | |
157 | CalcMaskSize(nMaskSize, nMaskCode); | |
158 | bool fFirst = vout.size() > 0 && !vout[0].IsNull(); | |
159 | bool fSecond = vout.size() > 1 && !vout[1].IsNull(); | |
160 | assert(fFirst || fSecond || nMaskCode); | |
161 | unsigned int nCode = 8*(nMaskCode - (fFirst || fSecond ? 0 : 1)) + (fCoinBase ? 1 : 0) + (fFirst ? 2 : 0) + (fSecond ? 4 : 0); | |
162 | // version | |
163 | nSize += ::GetSerializeSize(VARINT(this->nVersion), nType, nVersion); | |
164 | // size of header code | |
165 | nSize += ::GetSerializeSize(VARINT(nCode), nType, nVersion); | |
166 | // spentness bitmask | |
167 | nSize += nMaskSize; | |
168 | // txouts themself | |
169 | for (unsigned int i = 0; i < vout.size(); i++) | |
170 | if (!vout[i].IsNull()) | |
171 | nSize += ::GetSerializeSize(CTxOutCompressor(REF(vout[i])), nType, nVersion); | |
172 | // height | |
173 | nSize += ::GetSerializeSize(VARINT(nHeight), nType, nVersion); | |
174 | return nSize; | |
175 | } | |
176 | ||
177 | template<typename Stream> | |
178 | void Serialize(Stream &s, int nType, int nVersion) const { | |
179 | unsigned int nMaskSize = 0, nMaskCode = 0; | |
180 | CalcMaskSize(nMaskSize, nMaskCode); | |
181 | bool fFirst = vout.size() > 0 && !vout[0].IsNull(); | |
182 | bool fSecond = vout.size() > 1 && !vout[1].IsNull(); | |
183 | assert(fFirst || fSecond || nMaskCode); | |
184 | unsigned int nCode = 8*(nMaskCode - (fFirst || fSecond ? 0 : 1)) + (fCoinBase ? 1 : 0) + (fFirst ? 2 : 0) + (fSecond ? 4 : 0); | |
185 | // version | |
186 | ::Serialize(s, VARINT(this->nVersion), nType, nVersion); | |
187 | // header code | |
188 | ::Serialize(s, VARINT(nCode), nType, nVersion); | |
189 | // spentness bitmask | |
190 | for (unsigned int b = 0; b<nMaskSize; b++) { | |
191 | unsigned char chAvail = 0; | |
192 | for (unsigned int i = 0; i < 8 && 2+b*8+i < vout.size(); i++) | |
193 | if (!vout[2+b*8+i].IsNull()) | |
194 | chAvail |= (1 << i); | |
195 | ::Serialize(s, chAvail, nType, nVersion); | |
196 | } | |
197 | // txouts themself | |
198 | for (unsigned int i = 0; i < vout.size(); i++) { | |
199 | if (!vout[i].IsNull()) | |
200 | ::Serialize(s, CTxOutCompressor(REF(vout[i])), nType, nVersion); | |
201 | } | |
202 | // coinbase height | |
203 | ::Serialize(s, VARINT(nHeight), nType, nVersion); | |
204 | } | |
205 | ||
206 | template<typename Stream> | |
207 | void Unserialize(Stream &s, int nType, int nVersion) { | |
208 | unsigned int nCode = 0; | |
209 | // version | |
210 | ::Unserialize(s, VARINT(this->nVersion), nType, nVersion); | |
211 | // header code | |
212 | ::Unserialize(s, VARINT(nCode), nType, nVersion); | |
213 | fCoinBase = nCode & 1; | |
214 | std::vector<bool> vAvail(2, false); | |
8d657a65 E |
215 | vAvail[0] = (nCode & 2) != 0; |
216 | vAvail[1] = (nCode & 4) != 0; | |
a0fa20a1 PW |
217 | unsigned int nMaskCode = (nCode / 8) + ((nCode & 6) != 0 ? 0 : 1); |
218 | // spentness bitmask | |
219 | while (nMaskCode > 0) { | |
220 | unsigned char chAvail = 0; | |
221 | ::Unserialize(s, chAvail, nType, nVersion); | |
222 | for (unsigned int p = 0; p < 8; p++) { | |
223 | bool f = (chAvail & (1 << p)) != 0; | |
224 | vAvail.push_back(f); | |
225 | } | |
226 | if (chAvail != 0) | |
227 | nMaskCode--; | |
228 | } | |
229 | // txouts themself | |
230 | vout.assign(vAvail.size(), CTxOut()); | |
231 | for (unsigned int i = 0; i < vAvail.size(); i++) { | |
232 | if (vAvail[i]) | |
233 | ::Unserialize(s, REF(CTxOutCompressor(vout[i])), nType, nVersion); | |
234 | } | |
235 | // coinbase height | |
236 | ::Unserialize(s, VARINT(nHeight), nType, nVersion); | |
237 | Cleanup(); | |
238 | } | |
239 | ||
fa94b9d5 | 240 | //! mark a vout spent |
c444c620 | 241 | bool Spend(uint32_t nPos); |
a0fa20a1 | 242 | |
fa94b9d5 | 243 | //! check whether a particular output is still available |
a0fa20a1 PW |
244 | bool IsAvailable(unsigned int nPos) const { |
245 | return (nPos < vout.size() && !vout[nPos].IsNull()); | |
246 | } | |
247 | ||
fa94b9d5 MF |
248 | //! check whether the entire CCoins is spent |
249 | //! note that only !IsPruned() CCoins can be serialized | |
a0fa20a1 PW |
250 | bool IsPruned() const { |
251 | BOOST_FOREACH(const CTxOut &out, vout) | |
252 | if (!out.IsNull()) | |
253 | return false; | |
254 | return true; | |
255 | } | |
046392dc PW |
256 | |
257 | size_t DynamicMemoryUsage() const { | |
258 | size_t ret = memusage::DynamicUsage(vout); | |
259 | BOOST_FOREACH(const CTxOut &out, vout) { | |
260 | const std::vector<unsigned char> *script = &out.scriptPubKey; | |
261 | ret += memusage::DynamicUsage(*script); | |
262 | } | |
263 | return ret; | |
264 | } | |
a0fa20a1 PW |
265 | }; |
266 | ||
bc42503f PW |
267 | class CCoinsKeyHasher |
268 | { | |
269 | private: | |
270 | uint256 salt; | |
271 | ||
272 | public: | |
273 | CCoinsKeyHasher(); | |
fa94b9d5 MF |
274 | |
275 | /** | |
276 | * This *must* return size_t. With Boost 1.46 on 32-bit systems the | |
277 | * unordered_map will behave unpredictably if the custom hasher returns a | |
278 | * uint64_t, resulting in failures when syncing the chain (#4634). | |
279 | */ | |
6c23b082 | 280 | size_t operator()(const uint256& key) const { |
bc42503f PW |
281 | return key.GetHash(salt); |
282 | } | |
283 | }; | |
284 | ||
058b08c1 PW |
285 | struct CCoinsCacheEntry |
286 | { | |
287 | CCoins coins; // The actual cached data. | |
288 | unsigned char flags; | |
289 | ||
290 | enum Flags { | |
291 | DIRTY = (1 << 0), // This cache entry is potentially different from the version in the parent view. | |
292 | FRESH = (1 << 1), // The parent view does not have this entry (or it is pruned). | |
293 | }; | |
294 | ||
295 | CCoinsCacheEntry() : coins(), flags(0) {} | |
296 | }; | |
297 | ||
298 | typedef boost::unordered_map<uint256, CCoinsCacheEntry, CCoinsKeyHasher> CCoinsMap; | |
a0fa20a1 PW |
299 | |
300 | struct CCoinsStats | |
301 | { | |
302 | int nHeight; | |
303 | uint256 hashBlock; | |
304 | uint64_t nTransactions; | |
305 | uint64_t nTransactionOutputs; | |
306 | uint64_t nSerializedSize; | |
307 | uint256 hashSerialized; | |
a372168e | 308 | CAmount nTotalAmount; |
a0fa20a1 | 309 | |
4f152496 | 310 | CCoinsStats() : nHeight(0), nTransactions(0), nTransactionOutputs(0), nSerializedSize(0), nTotalAmount(0) {} |
a0fa20a1 PW |
311 | }; |
312 | ||
313 | ||
314 | /** Abstract view on the open txout dataset. */ | |
315 | class CCoinsView | |
316 | { | |
317 | public: | |
fa94b9d5 | 318 | //! Retrieve the CCoins (unspent transaction outputs) for a given txid |
a3dc587a | 319 | virtual bool GetCoins(const uint256 &txid, CCoins &coins) const; |
a0fa20a1 | 320 | |
fa94b9d5 MF |
321 | //! Just check whether we have data for a given txid. |
322 | //! This may (but cannot always) return true for fully spent transactions | |
a3dc587a | 323 | virtual bool HaveCoins(const uint256 &txid) const; |
a0fa20a1 | 324 | |
fa94b9d5 | 325 | //! Retrieve the block hash whose state this CCoinsView currently represents |
a3dc587a | 326 | virtual uint256 GetBestBlock() const; |
a0fa20a1 | 327 | |
fa94b9d5 MF |
328 | //! Do a bulk modification (multiple CCoins changes + BestBlock change). |
329 | //! The passed mapCoins can be modified. | |
b0875eb3 | 330 | virtual bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock); |
a0fa20a1 | 331 | |
fa94b9d5 | 332 | //! Calculate statistics about the unspent transaction output set |
a3dc587a | 333 | virtual bool GetStats(CCoinsStats &stats) const; |
a0fa20a1 | 334 | |
fa94b9d5 | 335 | //! As we use CCoinsViews polymorphically, have a virtual destructor |
a0fa20a1 PW |
336 | virtual ~CCoinsView() {} |
337 | }; | |
338 | ||
339 | ||
340 | /** CCoinsView backed by another CCoinsView */ | |
341 | class CCoinsViewBacked : public CCoinsView | |
342 | { | |
343 | protected: | |
344 | CCoinsView *base; | |
345 | ||
346 | public: | |
7c70438d | 347 | CCoinsViewBacked(CCoinsView *viewIn); |
a3dc587a | 348 | bool GetCoins(const uint256 &txid, CCoins &coins) const; |
a3dc587a DK |
349 | bool HaveCoins(const uint256 &txid) const; |
350 | uint256 GetBestBlock() const; | |
a0fa20a1 | 351 | void SetBackend(CCoinsView &viewIn); |
b0875eb3 | 352 | bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock); |
a3dc587a | 353 | bool GetStats(CCoinsStats &stats) const; |
a0fa20a1 PW |
354 | }; |
355 | ||
356 | ||
f28aec01 PW |
357 | class CCoinsViewCache; |
358 | ||
fa94b9d5 MF |
359 | /** |
360 | * A reference to a mutable cache entry. Encapsulating it allows us to run | |
f28aec01 | 361 | * cleanup code after the modification is finished, and keeping track of |
fa94b9d5 MF |
362 | * concurrent modifications. |
363 | */ | |
f28aec01 PW |
364 | class CCoinsModifier |
365 | { | |
366 | private: | |
367 | CCoinsViewCache& cache; | |
368 | CCoinsMap::iterator it; | |
046392dc PW |
369 | size_t cachedCoinUsage; // Cached memory usage of the CCoins object before modification |
370 | CCoinsModifier(CCoinsViewCache& cache_, CCoinsMap::iterator it_, size_t usage); | |
f28aec01 PW |
371 | |
372 | public: | |
058b08c1 PW |
373 | CCoins* operator->() { return &it->second.coins; } |
374 | CCoins& operator*() { return it->second.coins; } | |
f28aec01 PW |
375 | ~CCoinsModifier(); |
376 | friend class CCoinsViewCache; | |
377 | }; | |
378 | ||
a0fa20a1 PW |
379 | /** CCoinsView that adds a memory cache for transactions to another CCoinsView */ |
380 | class CCoinsViewCache : public CCoinsViewBacked | |
381 | { | |
382 | protected: | |
f28aec01 PW |
383 | /* Whether this cache has an active modifier. */ |
384 | bool hasModifier; | |
a3dc587a | 385 | |
046392dc | 386 | |
fa94b9d5 MF |
387 | /** |
388 | * Make mutable so that we can "fill the cache" even from Get-methods | |
389 | * declared as "const". | |
390 | */ | |
a3dc587a DK |
391 | mutable uint256 hashBlock; |
392 | mutable CCoinsMap cacheCoins; | |
a0fa20a1 | 393 | |
046392dc PW |
394 | /* Cached dynamic memory usage for the inner CCoins objects. */ |
395 | mutable size_t cachedCoinsUsage; | |
396 | ||
a0fa20a1 | 397 | public: |
7c70438d | 398 | CCoinsViewCache(CCoinsView *baseIn); |
f28aec01 | 399 | ~CCoinsViewCache(); |
a0fa20a1 PW |
400 | |
401 | // Standard CCoinsView methods | |
a3dc587a | 402 | bool GetCoins(const uint256 &txid, CCoins &coins) const; |
a3dc587a DK |
403 | bool HaveCoins(const uint256 &txid) const; |
404 | uint256 GetBestBlock() const; | |
c9d1a81c | 405 | void SetBestBlock(const uint256 &hashBlock); |
b0875eb3 | 406 | bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock); |
a0fa20a1 | 407 | |
fa94b9d5 MF |
408 | /** |
409 | * Return a pointer to CCoins in the cache, or NULL if not found. This is | |
410 | * more efficient than GetCoins. Modifications to other cache entries are | |
411 | * allowed while accessing the returned pointer. | |
412 | */ | |
629d75fa PW |
413 | const CCoins* AccessCoins(const uint256 &txid) const; |
414 | ||
fa94b9d5 MF |
415 | /** |
416 | * Return a modifiable reference to a CCoins. If no entry with the given | |
417 | * txid exists, a new one is created. Simultaneous modifications are not | |
418 | * allowed. | |
419 | */ | |
f28aec01 | 420 | CCoinsModifier ModifyCoins(const uint256 &txid); |
a0fa20a1 | 421 | |
fa94b9d5 MF |
422 | /** |
423 | * Push the modifications applied to this cache to its base. | |
424 | * Failure to call this method before destruction will cause the changes to be forgotten. | |
425 | * If false is returned, the state of this cache (and its backing view) will be undefined. | |
426 | */ | |
a0fa20a1 PW |
427 | bool Flush(); |
428 | ||
fa94b9d5 | 429 | //! Calculate the size of the cache (in number of transactions) |
a3dc587a | 430 | unsigned int GetCacheSize() const; |
a0fa20a1 | 431 | |
046392dc PW |
432 | //! Calculate the size of the cache (in bytes) |
433 | size_t DynamicMemoryUsage() const; | |
434 | ||
fa94b9d5 MF |
435 | /** |
436 | * Amount of bitcoins coming in to a transaction | |
437 | * Note that lightweight clients may not know anything besides the hash of previous transactions, | |
438 | * so may not be able to calculate this. | |
439 | * | |
440 | * @param[in] tx transaction for which we are checking input total | |
441 | * @return Sum of value of all inputs (scriptSigs) | |
a0fa20a1 | 442 | */ |
a372168e | 443 | CAmount GetValueIn(const CTransaction& tx) const; |
a0fa20a1 | 444 | |
fa94b9d5 | 445 | //! Check whether all prevouts of the transaction are present in the UTXO set represented by this view |
a3dc587a | 446 | bool HaveInputs(const CTransaction& tx) const; |
a0fa20a1 | 447 | |
fa94b9d5 | 448 | //! Return priority of tx at height nHeight |
a3dc587a | 449 | double GetPriority(const CTransaction &tx, int nHeight) const; |
4d707d51 | 450 | |
a3dc587a | 451 | const CTxOut &GetOutputFor(const CTxIn& input) const; |
a0fa20a1 | 452 | |
f28aec01 PW |
453 | friend class CCoinsModifier; |
454 | ||
a0fa20a1 | 455 | private: |
dd638dd7 | 456 | CCoinsMap::iterator FetchCoins(const uint256 &txid); |
a3dc587a | 457 | CCoinsMap::const_iterator FetchCoins(const uint256 &txid) const; |
228d2385 LD |
458 | |
459 | /** | |
460 | * By making the copy constructor private, we prevent accidentally using it when one intends to create a cache on top of a base cache. | |
461 | */ | |
462 | CCoinsViewCache(const CCoinsViewCache &); | |
a0fa20a1 PW |
463 | }; |
464 | ||
093303a8 | 465 | #endif // BITCOIN_COINS_H |