|
7 | 7 | #define BITCOIN_UNDO_H
|
8 | 8 |
|
9 | 9 | #include "compressor.h"
|
| 10 | +#include "consensus/consensus.h" |
10 | 11 | #include "primitives/transaction.h"
|
11 | 12 | #include "serialize.h"
|
12 | 13 |
|
13 | 14 | /** Undo information for a CTxIn
|
14 | 15 | *
|
15 | 16 | * Contains the prevout's CTxOut being spent, and its metadata as well
|
16 |
| - * (coinbase or not, height). Earlier versions also stored the transaction |
17 |
| - * version. |
| 17 | + * (coinbase or not, height). The serialization contains a dummy value of |
| 18 | + * zero. This is be compatible with older versions which expect to see |
| 19 | + * the transaction version there. |
18 | 20 | */
|
19 |
| -class CTxInUndo |
| 21 | +class TxInUndoSerializer |
20 | 22 | {
|
21 |
| -public: |
22 |
| - CTxOut txout; // the txout data before being spent |
23 |
| - bool fCoinBase; // if the outpoint was the last unspent: whether it belonged to a coinbase |
24 |
| - unsigned int nHeight; // if the outpoint was the last unspent: its height |
25 |
| - |
26 |
| - CTxInUndo() : txout(), fCoinBase(false), nHeight(0) {} |
27 |
| - CTxInUndo(const CTxOut &txoutIn, bool fCoinBaseIn = false, unsigned int nHeightIn = 0) : txout(txoutIn), fCoinBase(fCoinBaseIn), nHeight(nHeightIn) { } |
| 23 | + const Coin* txout; |
28 | 24 |
|
| 25 | +public: |
29 | 26 | template<typename Stream>
|
30 | 27 | void Serialize(Stream &s) const {
|
31 |
| - ::Serialize(s, VARINT(nHeight*2+(fCoinBase ? 1 : 0))); |
32 |
| - if (nHeight > 0) { |
33 |
| - int nVersionDummy = 0; |
34 |
| - ::Serialize(s, VARINT(nVersionDummy)); |
| 28 | + ::Serialize(s, VARINT(txout->nHeight * 2 + (txout->fCoinBase ? 1 : 0))); |
| 29 | + if (txout->nHeight > 0) { |
| 30 | + // Required to maintain compatibility with older undo format. |
| 31 | + ::Serialize(s, (unsigned char)0); |
35 | 32 | }
|
36 |
| - ::Serialize(s, CTxOutCompressor(REF(txout))); |
| 33 | + ::Serialize(s, CTxOutCompressor(REF(txout->out))); |
37 | 34 | }
|
38 | 35 |
|
| 36 | + TxInUndoSerializer(const Coin* coin) : txout(coin) {} |
| 37 | +}; |
| 38 | + |
| 39 | +class TxInUndoDeserializer |
| 40 | +{ |
| 41 | + Coin* txout; |
| 42 | + |
| 43 | +public: |
39 | 44 | template<typename Stream>
|
40 | 45 | void Unserialize(Stream &s) {
|
41 | 46 | unsigned int nCode = 0;
|
42 | 47 | ::Unserialize(s, VARINT(nCode));
|
43 |
| - nHeight = nCode / 2; |
44 |
| - fCoinBase = nCode & 1; |
45 |
| - if (nHeight > 0) { |
| 48 | + txout->nHeight = nCode / 2; |
| 49 | + txout->fCoinBase = nCode & 1; |
| 50 | + if (txout->nHeight > 0) { |
| 51 | + // Old versions stored the version number for the last spend of |
| 52 | + // a transaction's outputs. Non-final spends were indicated with |
| 53 | + // height = 0. |
46 | 54 | int nVersionDummy;
|
47 | 55 | ::Unserialize(s, VARINT(nVersionDummy));
|
48 | 56 | }
|
49 |
| - ::Unserialize(s, REF(CTxOutCompressor(REF(txout)))); |
| 57 | + ::Unserialize(s, REF(CTxOutCompressor(REF(txout->out)))); |
50 | 58 | }
|
| 59 | + |
| 60 | + TxInUndoDeserializer(Coin* coin) : txout(coin) {} |
51 | 61 | };
|
52 | 62 |
|
| 63 | +static const size_t MAX_INPUTS_PER_BLOCK = MAX_BLOCK_BASE_SIZE / ::GetSerializeSize(CTxIn(), SER_NETWORK, PROTOCOL_VERSION); |
| 64 | + |
53 | 65 | /** Undo information for a CTransaction */
|
54 | 66 | class CTxUndo
|
55 | 67 | {
|
56 | 68 | public:
|
57 | 69 | // undo information for all txins
|
58 |
| - std::vector<CTxInUndo> vprevout; |
| 70 | + std::vector<Coin> vprevout; |
59 | 71 |
|
60 |
| - ADD_SERIALIZE_METHODS; |
| 72 | + template <typename Stream> |
| 73 | + void Serialize(Stream& s) const { |
| 74 | + // TODO: avoid reimplementing vector serializer |
| 75 | + uint64_t count = vprevout.size(); |
| 76 | + ::Serialize(s, COMPACTSIZE(REF(count))); |
| 77 | + for (const auto& prevout : vprevout) { |
| 78 | + ::Serialize(s, REF(TxInUndoSerializer(&prevout))); |
| 79 | + } |
| 80 | + } |
61 | 81 |
|
62 |
| - template <typename Stream, typename Operation> |
63 |
| - inline void SerializationOp(Stream& s, Operation ser_action) { |
64 |
| - READWRITE(vprevout); |
| 82 | + template <typename Stream> |
| 83 | + void Unserialize(Stream& s) { |
| 84 | + // TODO: avoid reimplementing vector deserializer |
| 85 | + uint64_t count = 0; |
| 86 | + ::Unserialize(s, COMPACTSIZE(count)); |
| 87 | + if (count > MAX_INPUTS_PER_BLOCK) { |
| 88 | + throw std::ios_base::failure("Too many input undo records"); |
| 89 | + } |
| 90 | + vprevout.resize(count); |
| 91 | + for (auto& prevout : vprevout) { |
| 92 | + ::Unserialize(s, REF(TxInUndoDeserializer(&prevout))); |
| 93 | + } |
65 | 94 | }
|
66 | 95 | };
|
67 | 96 |
|
|
0 commit comments