mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-02-10 15:41:21 +00:00
Using VARINT with signed types is dangerous because negative values will appear to serialize correctly, but then deserialize as positive values mod 128. This commit changes the VARINT macro to trigger an error by default if called with an signed value, and updates broken uses of VARINT to pass a special flag that lets them keep working with no change in behavior.
113 lines
3.4 KiB
C++
113 lines
3.4 KiB
C++
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|
// Copyright (c) 2009-2017 The Bitcoin Core developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#ifndef BITCOIN_UNDO_H
|
|
#define BITCOIN_UNDO_H
|
|
|
|
#include <compressor.h>
|
|
#include <consensus/consensus.h>
|
|
#include <primitives/transaction.h>
|
|
#include <serialize.h>
|
|
|
|
/** Undo information for a CTxIn
|
|
*
|
|
* Contains the prevout's CTxOut being spent, and its metadata as well
|
|
* (coinbase or not, height). The serialization contains a dummy value of
|
|
* zero. This is be compatible with older versions which expect to see
|
|
* the transaction version there.
|
|
*/
|
|
class TxInUndoSerializer
|
|
{
|
|
const Coin* txout;
|
|
|
|
public:
|
|
template<typename Stream>
|
|
void Serialize(Stream &s) const {
|
|
::Serialize(s, VARINT(txout->nHeight * 2 + (txout->fCoinBase ? 1 : 0), VarIntMode::NONNEGATIVE_SIGNED));
|
|
if (txout->nHeight > 0) {
|
|
// Required to maintain compatibility with older undo format.
|
|
::Serialize(s, (unsigned char)0);
|
|
}
|
|
::Serialize(s, CTxOutCompressor(REF(txout->out)));
|
|
}
|
|
|
|
explicit TxInUndoSerializer(const Coin* coin) : txout(coin) {}
|
|
};
|
|
|
|
class TxInUndoDeserializer
|
|
{
|
|
Coin* txout;
|
|
|
|
public:
|
|
template<typename Stream>
|
|
void Unserialize(Stream &s) {
|
|
unsigned int nCode = 0;
|
|
::Unserialize(s, VARINT(nCode));
|
|
txout->nHeight = nCode / 2;
|
|
txout->fCoinBase = nCode & 1;
|
|
if (txout->nHeight > 0) {
|
|
// Old versions stored the version number for the last spend of
|
|
// a transaction's outputs. Non-final spends were indicated with
|
|
// height = 0.
|
|
unsigned int nVersionDummy;
|
|
::Unserialize(s, VARINT(nVersionDummy));
|
|
}
|
|
::Unserialize(s, CTxOutCompressor(REF(txout->out)));
|
|
}
|
|
|
|
explicit TxInUndoDeserializer(Coin* coin) : txout(coin) {}
|
|
};
|
|
|
|
static const size_t MIN_TRANSACTION_INPUT_WEIGHT = WITNESS_SCALE_FACTOR * ::GetSerializeSize(CTxIn(), SER_NETWORK, PROTOCOL_VERSION);
|
|
static const size_t MAX_INPUTS_PER_BLOCK = MAX_BLOCK_WEIGHT / MIN_TRANSACTION_INPUT_WEIGHT;
|
|
|
|
/** Undo information for a CTransaction */
|
|
class CTxUndo
|
|
{
|
|
public:
|
|
// undo information for all txins
|
|
std::vector<Coin> vprevout;
|
|
|
|
template <typename Stream>
|
|
void Serialize(Stream& s) const {
|
|
// TODO: avoid reimplementing vector serializer
|
|
uint64_t count = vprevout.size();
|
|
::Serialize(s, COMPACTSIZE(REF(count)));
|
|
for (const auto& prevout : vprevout) {
|
|
::Serialize(s, TxInUndoSerializer(&prevout));
|
|
}
|
|
}
|
|
|
|
template <typename Stream>
|
|
void Unserialize(Stream& s) {
|
|
// TODO: avoid reimplementing vector deserializer
|
|
uint64_t count = 0;
|
|
::Unserialize(s, COMPACTSIZE(count));
|
|
if (count > MAX_INPUTS_PER_BLOCK) {
|
|
throw std::ios_base::failure("Too many input undo records");
|
|
}
|
|
vprevout.resize(count);
|
|
for (auto& prevout : vprevout) {
|
|
::Unserialize(s, TxInUndoDeserializer(&prevout));
|
|
}
|
|
}
|
|
};
|
|
|
|
/** Undo information for a CBlock */
|
|
class CBlockUndo
|
|
{
|
|
public:
|
|
std::vector<CTxUndo> vtxundo; // for all but the coinbase
|
|
|
|
ADD_SERIALIZE_METHODS;
|
|
|
|
template <typename Stream, typename Operation>
|
|
inline void SerializationOp(Stream& s, Operation ser_action) {
|
|
READWRITE(vtxundo);
|
|
}
|
|
};
|
|
|
|
#endif // BITCOIN_UNDO_H
|