diff options
author | Pieter Wuille <pieter.wuille@gmail.com> | 2015-11-06 01:32:04 +0100 |
---|---|---|
committer | Pieter Wuille <pieter.wuille@gmail.com> | 2016-06-22 15:42:59 +0200 |
commit | 7030d9eb47254499bba14f1c00abc6bf493efd91 (patch) | |
tree | 95055978907c7418c1b96728ff09413781b9c0b7 /src/primitives | |
parent | ecacfd98e657c5819a1bcb1da93b25d3ad4e5045 (diff) |
BIP144: Serialization, hashes, relay (sender side)
Contains refactorings by Eric Lombrozo.
Contains fixup by Nicolas Dorier.
Contains cleanup of CInv::GetCommand by Alex Morcos
Diffstat (limited to 'src/primitives')
-rw-r--r-- | src/primitives/block.h | 2 | ||||
-rw-r--r-- | src/primitives/transaction.cpp | 16 | ||||
-rw-r--r-- | src/primitives/transaction.h | 151 |
3 files changed, 152 insertions, 17 deletions
diff --git a/src/primitives/block.h b/src/primitives/block.h index 42276b2bc2..29307aed5b 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -38,7 +38,6 @@ public: template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { READWRITE(this->nVersion); - nVersion = this->nVersion; READWRITE(hashPrevBlock); READWRITE(hashMerkleRoot); READWRITE(nTime); @@ -120,7 +119,6 @@ public: std::string ToString() const; }; - /** Describes a place in the block chain to another node such that if the * other node doesn't have the same branch, it can find a recent common trunk. * The further back it is, the further before the fork it may be. diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index 947f2e6a73..b0230530e8 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -60,21 +60,26 @@ std::string CTxOut::ToString() const } CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::CURRENT_VERSION), nLockTime(0) {} -CMutableTransaction::CMutableTransaction(const CTransaction& tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime) {} +CMutableTransaction::CMutableTransaction(const CTransaction& tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), wit(tx.wit), nLockTime(tx.nLockTime) {} uint256 CMutableTransaction::GetHash() const { - return SerializeHash(*this); + return SerializeHash(*this, SER_GETHASH, SERIALIZE_TRANSACTION_NO_WITNESS); } void CTransaction::UpdateHash() const { - *const_cast<uint256*>(&hash) = SerializeHash(*this); + *const_cast<uint256*>(&hash) = SerializeHash(*this, SER_GETHASH, SERIALIZE_TRANSACTION_NO_WITNESS); +} + +uint256 CTransaction::GetWitnessHash() const +{ + return SerializeHash(*this, SER_GETHASH, 0); } CTransaction::CTransaction() : nVersion(CTransaction::CURRENT_VERSION), vin(), vout(), nLockTime(0) { } -CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime) { +CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), wit(tx.wit), nLockTime(tx.nLockTime) { UpdateHash(); } @@ -82,6 +87,7 @@ CTransaction& CTransaction::operator=(const CTransaction &tx) { *const_cast<int*>(&nVersion) = tx.nVersion; *const_cast<std::vector<CTxIn>*>(&vin) = tx.vin; *const_cast<std::vector<CTxOut>*>(&vout) = tx.vout; + *const_cast<CTxWitness*>(&wit) = tx.wit; *const_cast<unsigned int*>(&nLockTime) = tx.nLockTime; *const_cast<uint256*>(&hash) = tx.hash; return *this; @@ -136,6 +142,8 @@ std::string CTransaction::ToString() const nLockTime); for (unsigned int i = 0; i < vin.size(); i++) str += " " + vin[i].ToString() + "\n"; + for (unsigned int i = 0; i < wit.vtxinwit.size(); i++) + str += " " + wit.vtxinwit[i].scriptWitness.ToString() + "\n"; for (unsigned int i = 0; i < vout.size(); i++) str += " " + vout[i].ToString() + "\n"; return str; diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 149816406a..d8ae41ad78 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -11,6 +11,8 @@ #include "serialize.h" #include "uint256.h" +static const int SERIALIZE_TRANSACTION_NO_WITNESS = 0x40000000; + /** An outpoint - a combination of a transaction hash and an index n into its vout */ class COutPoint { @@ -194,8 +196,137 @@ public: std::string ToString() const; }; +class CTxinWitness +{ +public: + CScriptWitness scriptWitness; + + ADD_SERIALIZE_METHODS; + + template <typename Stream, typename Operation> + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) + { + READWRITE(scriptWitness.stack); + } + + bool IsNull() const { return scriptWitness.IsNull(); } + + CTxinWitness() { } +}; + +class CTxWitness +{ +public: + /** In case vtxinwit is missing, all entries are treated as if they were empty CTxInWitnesses */ + std::vector<CTxinWitness> vtxinwit; + + ADD_SERIALIZE_METHODS; + + bool IsEmpty() const { return vtxinwit.empty(); } + + bool IsNull() const + { + for (size_t n = 0; n < vtxinwit.size(); n++) { + if (!vtxinwit[n].IsNull()) { + return false; + } + } + return true; + } + + void SetNull() + { + vtxinwit.clear(); + } + + template <typename Stream, typename Operation> + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) + { + for (size_t n = 0; n < vtxinwit.size(); n++) { + READWRITE(vtxinwit[n]); + } + if (IsNull()) { + /* It's illegal to encode a witness when all vtxinwit entries are empty. */ + throw std::ios_base::failure("Superfluous witness record"); + } + } +}; + struct CMutableTransaction; +/** + * Basic transaction serialization format: + * - int32_t nVersion + * - std::vector<CTxIn> vin + * - std::vector<CTxOut> vout + * - uint32_t nLockTime + * + * Extended transaction serialization format: + * - int32_t nVersion + * - unsigned char dummy = 0x00 + * - unsigned char flags (!= 0) + * - std::vector<CTxIn> vin + * - std::vector<CTxOut> vout + * - if (flags & 1): + * - CTxWitness wit; + * - uint32_t nLockTime + */ +template<typename Stream, typename Operation, typename TxType> +inline void SerializeTransaction(TxType& tx, Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(*const_cast<int32_t*>(&tx.nVersion)); + unsigned char flags = 0; + if (ser_action.ForRead()) { + const_cast<std::vector<CTxIn>*>(&tx.vin)->clear(); + const_cast<std::vector<CTxOut>*>(&tx.vout)->clear(); + const_cast<CTxWitness*>(&tx.wit)->SetNull(); + /* Try to read the vin. In case the dummy is there, this will be read as an empty vector. */ + READWRITE(*const_cast<std::vector<CTxIn>*>(&tx.vin)); + if (tx.vin.size() == 0 && !(nVersion & SERIALIZE_TRANSACTION_NO_WITNESS)) { + /* We read a dummy or an empty vin. */ + READWRITE(flags); + if (flags != 0) { + READWRITE(*const_cast<std::vector<CTxIn>*>(&tx.vin)); + READWRITE(*const_cast<std::vector<CTxOut>*>(&tx.vout)); + } + } else { + /* We read a non-empty vin. Assume a normal vout follows. */ + READWRITE(*const_cast<std::vector<CTxOut>*>(&tx.vout)); + } + if ((flags & 1) && !(nVersion & SERIALIZE_TRANSACTION_NO_WITNESS)) { + /* The witness flag is present, and we support witnesses. */ + flags ^= 1; + const_cast<CTxWitness*>(&tx.wit)->vtxinwit.resize(tx.vin.size()); + READWRITE(tx.wit); + } + if (flags) { + /* Unknown flag in the serialization */ + throw std::ios_base::failure("Unknown transaction optional data"); + } + } else { + // Consistency check + assert(tx.wit.vtxinwit.size() <= tx.vin.size()); + if (!(nVersion & SERIALIZE_TRANSACTION_NO_WITNESS)) { + /* Check whether witnesses need to be serialized. */ + if (!tx.wit.IsNull()) { + flags |= 1; + } + } + if (flags) { + /* Use extended format in case witnesses are to be serialized. */ + std::vector<CTxIn> vinDummy; + READWRITE(vinDummy); + READWRITE(flags); + } + READWRITE(*const_cast<std::vector<CTxIn>*>(&tx.vin)); + READWRITE(*const_cast<std::vector<CTxOut>*>(&tx.vout)); + if (flags & 1) { + const_cast<CTxWitness*>(&tx.wit)->vtxinwit.resize(tx.vin.size()); + READWRITE(tx.wit); + } + } + READWRITE(*const_cast<uint32_t*>(&tx.nLockTime)); +} + /** The basic transaction that is broadcasted on the network and contained in * blocks. A transaction can contain multiple inputs and outputs. */ @@ -224,6 +355,7 @@ public: const int32_t nVersion; const std::vector<CTxIn> vin; const std::vector<CTxOut> vout; + CTxWitness wit; // Not const: can change without invalidating the txid cache const uint32_t nLockTime; /** Construct a CTransaction that qualifies as IsNull() */ @@ -238,13 +370,10 @@ public: template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - READWRITE(*const_cast<int32_t*>(&this->nVersion)); - nVersion = this->nVersion; - READWRITE(*const_cast<std::vector<CTxIn>*>(&vin)); - READWRITE(*const_cast<std::vector<CTxOut>*>(&vout)); - READWRITE(*const_cast<uint32_t*>(&nLockTime)); - if (ser_action.ForRead()) + SerializeTransaction(*this, s, ser_action, nType, nVersion); + if (ser_action.ForRead()) { UpdateHash(); + } } bool IsNull() const { @@ -255,6 +384,9 @@ public: return hash; } + // Compute a hash that includes both transaction and witness data + uint256 GetWitnessHash() const; + // Return sum of txouts. CAmount GetValueOut() const; // GetValueIn() is a method on CCoinsViewCache, because @@ -290,6 +422,7 @@ struct CMutableTransaction int32_t nVersion; std::vector<CTxIn> vin; std::vector<CTxOut> vout; + CTxWitness wit; uint32_t nLockTime; CMutableTransaction(); @@ -299,11 +432,7 @@ struct CMutableTransaction template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - READWRITE(this->nVersion); - nVersion = this->nVersion; - READWRITE(vin); - READWRITE(vout); - READWRITE(nLockTime); + SerializeTransaction(*this, s, ser_action, nType, nVersion); } /** Compute the hash of this CMutableTransaction. This is computed on the |