mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-06-03 17:54:19 +02:00
Make CTransaction actually immutable
This commit is contained in:
@@ -62,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
|
||||
@@ -72,25 +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(CMutableTransaction &&tx) : nVersion(tx.nVersion), vin(std::move(tx.vin)), vout(std::move(tx.vout)), wit(std::move(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
|
||||
{
|
||||
|
||||
@@ -286,73 +286,81 @@ struct CMutableTransaction;
|
||||
* - CTxWitness wit;
|
||||
* - uint32_t nLockTime
|
||||
*/
|
||||
template<typename Stream, typename Operation, typename TxType>
|
||||
inline void SerializeTransaction(TxType& tx, Stream& s, Operation ser_action) {
|
||||
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);
|
||||
}
|
||||
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);
|
||||
/* 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<uint32_t*>(&tx.nLockTime));
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
@@ -374,6 +382,13 @@ 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();
|
||||
|
||||
@@ -381,18 +396,13 @@ public:
|
||||
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) {
|
||||
SerializeTransaction(*this, s, ser_action);
|
||||
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)) {}
|
||||
|
||||
@@ -417,7 +427,7 @@ 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.
|
||||
@@ -441,8 +451,6 @@ public:
|
||||
}
|
||||
|
||||
std::string ToString() const;
|
||||
|
||||
void UpdateHash() const;
|
||||
};
|
||||
|
||||
/** A mutable version of CTransaction. */
|
||||
@@ -457,11 +465,15 @@ 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) {
|
||||
SerializeTransaction(*this, s, ser_action);
|
||||
|
||||
template <typename Stream>
|
||||
inline void Unserialize(Stream& s) {
|
||||
UnserializeTransaction(*this, s);
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
|
||||
Reference in New Issue
Block a user