aboutsummaryrefslogtreecommitdiff
path: root/src/primitives
diff options
context:
space:
mode:
authorPieter Wuille <pieter.wuille@gmail.com>2015-11-06 01:32:04 +0100
committerPieter Wuille <pieter.wuille@gmail.com>2016-06-22 15:42:59 +0200
commit7030d9eb47254499bba14f1c00abc6bf493efd91 (patch)
tree95055978907c7418c1b96728ff09413781b9c0b7 /src/primitives
parentecacfd98e657c5819a1bcb1da93b25d3ad4e5045 (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.h2
-rw-r--r--src/primitives/transaction.cpp16
-rw-r--r--src/primitives/transaction.h151
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