diff options
Diffstat (limited to 'src/primitives')
| -rw-r--r-- | src/primitives/block.cpp | 2 | ||||
| -rw-r--r-- | src/primitives/block.h | 11 | ||||
| -rw-r--r-- | src/primitives/transaction.cpp | 33 | ||||
| -rw-r--r-- | src/primitives/transaction.h | 188 |
4 files changed, 128 insertions, 106 deletions
diff --git a/src/primitives/block.cpp b/src/primitives/block.cpp index 0e6ab4dd7..95bd2211f 100644 --- a/src/primitives/block.cpp +++ b/src/primitives/block.cpp @@ -27,7 +27,7 @@ std::string CBlock::ToString() const vtx.size()); for (unsigned int i = 0; i < vtx.size(); i++) { - s << " " << vtx[i].ToString() << "\n"; + s << " " << vtx[i]->ToString() << "\n"; } return s.str(); } diff --git a/src/primitives/block.h b/src/primitives/block.h index 72dfed985..b037fc839 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -36,7 +36,7 @@ public: ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> - inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + inline void SerializationOp(Stream& s, Operation ser_action) { READWRITE(this->nVersion); READWRITE(hashPrevBlock); READWRITE(hashMerkleRoot); @@ -73,7 +73,7 @@ class CBlock : public CBlockHeader { public: // network and disk - std::vector<CTransaction> vtx; + std::vector<CTransactionRef> vtx; // memory only mutable bool fChecked; @@ -92,7 +92,7 @@ public: ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> - inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + inline void SerializationOp(Stream& s, Operation ser_action) { READWRITE(*(CBlockHeader*)this); READWRITE(vtx); } @@ -137,8 +137,9 @@ struct CBlockLocator ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> - inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - if (!(nType & SER_GETHASH)) + inline void SerializationOp(Stream& s, Operation ser_action) { + int nVersion = s.GetVersion(); + if (!(s.GetType() & SER_GETHASH)) READWRITE(nVersion); READWRITE(vHave); } diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index 2fdc59ea0..11d7eace5 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -49,11 +49,6 @@ CTxOut::CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn) scriptPubKey = scriptPubKeyIn; } -uint256 CTxOut::GetHash() const -{ - return SerializeHash(*this); -} - std::string CTxOut::ToString() const { return strprintf("CTxOut(nValue=%d.%08d, scriptPubKey=%s)", nValue / COIN, nValue % COIN, HexStr(scriptPubKey).substr(0, 30)); @@ -67,9 +62,9 @@ uint256 CMutableTransaction::GetHash() const return SerializeHash(*this, SER_GETHASH, SERIALIZE_TRANSACTION_NO_WITNESS); } -void CTransaction::UpdateHash() const +uint256 CTransaction::ComputeHash() const { - *const_cast<uint256*>(&hash) = SerializeHash(*this, SER_GETHASH, SERIALIZE_TRANSACTION_NO_WITNESS); + return SerializeHash(*this, SER_GETHASH, SERIALIZE_TRANSACTION_NO_WITNESS); } uint256 CTransaction::GetWitnessHash() const @@ -77,21 +72,10 @@ 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), wit(tx.wit), nLockTime(tx.nLockTime) { - UpdateHash(); -} - -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; -} +/* For backward compatibility, the hash is initialized to 0. TODO: remove the need for this default constructor entirely. */ +CTransaction::CTransaction() : nVersion(CTransaction::CURRENT_VERSION), vin(), vout(), nLockTime(0), hash() {} +CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), wit(tx.wit), nLockTime(tx.nLockTime), hash(ComputeHash()) {} +CTransaction::CTransaction(CMutableTransaction &&tx) : nVersion(tx.nVersion), vin(std::move(tx.vin)), vout(std::move(tx.vout)), wit(std::move(tx.wit)), nLockTime(tx.nLockTime), hash(ComputeHash()) {} CAmount CTransaction::GetValueOut() const { @@ -131,6 +115,11 @@ unsigned int CTransaction::CalculateModifiedSize(unsigned int nTxSize) const return nTxSize; } +unsigned int CTransaction::GetTotalSize() const +{ + return ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION); +} + std::string CTransaction::ToString() const { std::string str; diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 5689d15bf..b594cab45 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -28,7 +28,7 @@ public: ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> - inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + inline void SerializationOp(Stream& s, Operation ser_action) { READWRITE(hash); READWRITE(n); } @@ -104,7 +104,7 @@ public: ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> - inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + inline void SerializationOp(Stream& s, Operation ser_action) { READWRITE(prevout); READWRITE(*(CScriptBase*)(&scriptSig)); READWRITE(nSequence); @@ -144,7 +144,7 @@ public: ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> - inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + inline void SerializationOp(Stream& s, Operation ser_action) { READWRITE(nValue); READWRITE(*(CScriptBase*)(&scriptPubKey)); } @@ -160,8 +160,6 @@ public: return (nValue == -1); } - uint256 GetHash() const; - CAmount GetDustThreshold(const CFeeRate &minRelayTxFee) const { // "Dust" is defined in terms of CTransaction::minRelayTxFee, @@ -179,7 +177,7 @@ public: if (scriptPubKey.IsUnspendable()) return 0; - size_t nSize = GetSerializeSize(SER_DISK, 0); + size_t nSize = GetSerializeSize(*this, SER_DISK, 0); int witnessversion = 0; std::vector<unsigned char> witnessprogram; @@ -221,7 +219,7 @@ public: ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> - inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) + inline void SerializationOp(Stream& s, Operation ser_action) { READWRITE(scriptWitness.stack); } @@ -257,7 +255,7 @@ public: } template <typename Stream, typename Operation> - inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) + inline void SerializationOp(Stream& s, Operation ser_action) { for (size_t n = 0; n < vtxinwit.size(); n++) { READWRITE(vtxinwit[n]); @@ -288,76 +286,84 @@ struct CMutableTransaction; * - 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) { - const bool fAllowWitness = !(nVersion & SERIALIZE_TRANSACTION_NO_WITNESS); +template<typename Stream, typename TxType> +inline void UnserializeTransaction(TxType& tx, Stream& s) { + const bool fAllowWitness = !(s.GetVersion() & SERIALIZE_TRANSACTION_NO_WITNESS); - READWRITE(*const_cast<int32_t*>(&tx.nVersion)); + s >> 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 && fAllowWitness) { - /* 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) && fAllowWitness) { - /* 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"); + tx.vin.clear(); + tx.vout.clear(); + tx.wit.SetNull(); + /* Try to read the vin. In case the dummy is there, this will be read as an empty vector. */ + s >> tx.vin; + if (tx.vin.size() == 0 && fAllowWitness) { + /* We read a dummy or an empty vin. */ + s >> flags; + if (flags != 0) { + s >> tx.vin; + s >> tx.vout; } } else { - // Consistency check - assert(tx.wit.vtxinwit.size() <= tx.vin.size()); - if (fAllowWitness) { - /* 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); + /* We read a non-empty vin. Assume a normal vout follows. */ + s >> tx.vout; + } + if ((flags & 1) && fAllowWitness) { + /* The witness flag is present, and we support witnesses. */ + flags ^= 1; + tx.wit.vtxinwit.resize(tx.vin.size()); + s >> tx.wit; + } + if (flags) { + /* Unknown flag in the serialization */ + throw std::ios_base::failure("Unknown transaction optional data"); + } + s >> tx.nLockTime; +} + +template<typename Stream, typename TxType> +inline void SerializeTransaction(const TxType& tx, Stream& s) { + const bool fAllowWitness = !(s.GetVersion() & SERIALIZE_TRANSACTION_NO_WITNESS); + + s << tx.nVersion; + unsigned char flags = 0; + // Consistency check + assert(tx.wit.vtxinwit.size() <= tx.vin.size()); + if (fAllowWitness) { + /* Check whether witnesses need to be serialized. */ + if (!tx.wit.IsNull()) { + flags |= 1; } - 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); + } + if (flags) { + /* Use extended format in case witnesses are to be serialized. */ + std::vector<CTxIn> vinDummy; + s << vinDummy; + s << flags; + } + s << tx.vin; + s << tx.vout; + if (flags & 1) { + for (size_t i = 0; i < tx.vin.size(); i++) { + if (i < tx.wit.vtxinwit.size()) { + s << tx.wit.vtxinwit[i]; + } else { + s << CTxInWitness(); + } } } - READWRITE(*const_cast<uint32_t*>(&tx.nLockTime)); + s << tx.nLockTime; } + /** The basic transaction that is broadcasted on the network and contained in * blocks. A transaction can contain multiple inputs and outputs. */ class CTransaction { -private: - /** Memory only. */ - const uint256 hash; - public: // Default transaction version. - static const int32_t CURRENT_VERSION=1; + static const int32_t CURRENT_VERSION=2; // Changing the default transaction version requires a two step process: first // adapting relay policy by bumping MAX_STANDARD_VERSION, and then later date @@ -376,24 +382,30 @@ public: CTxWitness wit; // Not const: can change without invalidating the txid cache const uint32_t nLockTime; +private: + /** Memory only. */ + const uint256 hash; + + uint256 ComputeHash() const; + +public: /** Construct a CTransaction that qualifies as IsNull() */ CTransaction(); /** Convert a CMutableTransaction into a CTransaction. */ CTransaction(const CMutableTransaction &tx); + CTransaction(CMutableTransaction &&tx); - CTransaction& operator=(const CTransaction& tx); - - ADD_SERIALIZE_METHODS; - - template <typename Stream, typename Operation> - inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - SerializeTransaction(*this, s, ser_action, nType, nVersion); - if (ser_action.ForRead()) { - UpdateHash(); - } + template <typename Stream> + inline void Serialize(Stream& s) const { + SerializeTransaction(*this, s); } + /** This deserializing constructor is provided instead of an Unserialize method. + * Unserialize is not possible, since it would require overwriting const fields. */ + template <typename Stream> + CTransaction(deserialize_type, Stream& s) : CTransaction(CMutableTransaction(deserialize, s)) {} + bool IsNull() const { return vin.empty() && vout.empty(); } @@ -416,6 +428,13 @@ public: // Compute modified tx size for priority calculation (optionally given tx size) unsigned int CalculateModifiedSize(unsigned int nTxSize=0) const; + /** + * Get the total transaction size in bytes, including witness data. + * "Total Size" defined in BIP141 and BIP144. + * @return Total transaction size in bytes + */ + unsigned int GetTotalSize() const; + bool IsCoinBase() const { return (vin.size() == 1 && vin[0].prevout.IsNull()); @@ -432,8 +451,6 @@ public: } std::string ToString() const; - - void UpdateHash() const; }; /** A mutable version of CTransaction. */ @@ -448,11 +465,20 @@ struct CMutableTransaction CMutableTransaction(); CMutableTransaction(const CTransaction& tx); - ADD_SERIALIZE_METHODS; + template <typename Stream> + inline void Serialize(Stream& s) const { + SerializeTransaction(*this, s); + } - template <typename Stream, typename Operation> - inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - SerializeTransaction(*this, s, ser_action, nType, nVersion); + + template <typename Stream> + inline void Unserialize(Stream& s) { + UnserializeTransaction(*this, s); + } + + template <typename Stream> + CMutableTransaction(deserialize_type, Stream& s) { + Unserialize(s); } /** Compute the hash of this CMutableTransaction. This is computed on the @@ -461,6 +487,12 @@ struct CMutableTransaction uint256 GetHash() const; }; +typedef std::shared_ptr<const CTransaction> CTransactionRef; +static inline CTransactionRef MakeTransactionRef() { return std::make_shared<const CTransaction>(); } +template <typename Tx> static inline CTransactionRef MakeTransactionRef(Tx&& txIn) { return std::make_shared<const CTransaction>(std::forward<Tx>(txIn)); } +static inline CTransactionRef MakeTransactionRef(const CTransactionRef& txIn) { return txIn; } +static inline CTransactionRef MakeTransactionRef(CTransactionRef&& txIn) { return std::move(txIn); } + /** Compute the weight of a transaction, as defined by BIP 141 */ int64_t GetTransactionWeight(const CTransaction &tx); |