Major updates integration from all upstreams
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
#include "random.h"
|
||||
#include "script/script.h"
|
||||
#include "serialize.h"
|
||||
#include "streams.h"
|
||||
#include "uint256.h"
|
||||
#include "arith_uint256.h"
|
||||
#include "consensus/consensus.h"
|
||||
@@ -20,7 +21,9 @@
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#include <boost/array.hpp>
|
||||
#include <array>
|
||||
|
||||
#include <boost/variant.hpp>
|
||||
|
||||
#include "zcash/NoteEncryption.hpp"
|
||||
#include "zcash/Zcash.h"
|
||||
@@ -29,6 +32,159 @@
|
||||
|
||||
extern uint32_t ASSETCHAINS_MAGIC;
|
||||
|
||||
// Overwinter transaction version
|
||||
static const int32_t OVERWINTER_TX_VERSION = 3;
|
||||
static_assert(OVERWINTER_TX_VERSION >= OVERWINTER_MIN_TX_VERSION,
|
||||
"Overwinter tx version must not be lower than minimum");
|
||||
static_assert(OVERWINTER_TX_VERSION <= OVERWINTER_MAX_TX_VERSION,
|
||||
"Overwinter tx version must not be higher than maximum");
|
||||
|
||||
// Sapling transaction version
|
||||
static const int32_t SAPLING_TX_VERSION = 4;
|
||||
static_assert(SAPLING_TX_VERSION >= SAPLING_MIN_TX_VERSION,
|
||||
"Sapling tx version must not be lower than minimum");
|
||||
static_assert(SAPLING_TX_VERSION <= SAPLING_MAX_TX_VERSION,
|
||||
"Sapling tx version must not be higher than maximum");
|
||||
|
||||
/**
|
||||
* A shielded input to a transaction. It contains data that describes a Spend transfer.
|
||||
*/
|
||||
class SpendDescription
|
||||
{
|
||||
public:
|
||||
typedef std::array<unsigned char, 64> spend_auth_sig_t;
|
||||
|
||||
uint256 cv; //!< A value commitment to the value of the input note.
|
||||
uint256 anchor; //!< A Merkle root of the Sapling note commitment tree at some block height in the past.
|
||||
uint256 nullifier; //!< The nullifier of the input note.
|
||||
uint256 rk; //!< The randomized public key for spendAuthSig.
|
||||
libzcash::GrothProof zkproof; //!< A zero-knowledge proof using the spend circuit.
|
||||
spend_auth_sig_t spendAuthSig; //!< A signature authorizing this spend.
|
||||
|
||||
SpendDescription() { }
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(cv);
|
||||
READWRITE(anchor);
|
||||
READWRITE(nullifier);
|
||||
READWRITE(rk);
|
||||
READWRITE(zkproof);
|
||||
READWRITE(spendAuthSig);
|
||||
}
|
||||
|
||||
friend bool operator==(const SpendDescription& a, const SpendDescription& b)
|
||||
{
|
||||
return (
|
||||
a.cv == b.cv &&
|
||||
a.anchor == b.anchor &&
|
||||
a.nullifier == b.nullifier &&
|
||||
a.rk == b.rk &&
|
||||
a.zkproof == b.zkproof &&
|
||||
a.spendAuthSig == b.spendAuthSig
|
||||
);
|
||||
}
|
||||
|
||||
friend bool operator!=(const SpendDescription& a, const SpendDescription& b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A shielded output to a transaction. It contains data that describes an Output transfer.
|
||||
*/
|
||||
class OutputDescription
|
||||
{
|
||||
public:
|
||||
uint256 cv; //!< A value commitment to the value of the output note.
|
||||
uint256 cm; //!< The note commitment for the output note.
|
||||
uint256 ephemeralKey; //!< A Jubjub public key.
|
||||
libzcash::SaplingEncCiphertext encCiphertext; //!< A ciphertext component for the encrypted output note.
|
||||
libzcash::SaplingOutCiphertext outCiphertext; //!< A ciphertext component for the encrypted output note.
|
||||
libzcash::GrothProof zkproof; //!< A zero-knowledge proof using the output circuit.
|
||||
|
||||
OutputDescription() { }
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(cv);
|
||||
READWRITE(cm);
|
||||
READWRITE(ephemeralKey);
|
||||
READWRITE(encCiphertext);
|
||||
READWRITE(outCiphertext);
|
||||
READWRITE(zkproof);
|
||||
}
|
||||
|
||||
friend bool operator==(const OutputDescription& a, const OutputDescription& b)
|
||||
{
|
||||
return (
|
||||
a.cv == b.cv &&
|
||||
a.cm == b.cm &&
|
||||
a.ephemeralKey == b.ephemeralKey &&
|
||||
a.encCiphertext == b.encCiphertext &&
|
||||
a.outCiphertext == b.outCiphertext &&
|
||||
a.zkproof == b.zkproof
|
||||
);
|
||||
}
|
||||
|
||||
friend bool operator!=(const OutputDescription& a, const OutputDescription& b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Stream>
|
||||
class SproutProofSerializer : public boost::static_visitor<>
|
||||
{
|
||||
Stream& s;
|
||||
bool useGroth;
|
||||
|
||||
public:
|
||||
SproutProofSerializer(Stream& s, bool useGroth) : s(s), useGroth(useGroth) {}
|
||||
|
||||
void operator()(const libzcash::PHGRProof& proof) const
|
||||
{
|
||||
if (useGroth) {
|
||||
throw std::ios_base::failure("Invalid Sprout proof for transaction format (expected GrothProof, found PHGRProof)");
|
||||
}
|
||||
::Serialize(s, proof);
|
||||
}
|
||||
|
||||
void operator()(const libzcash::GrothProof& proof) const
|
||||
{
|
||||
if (!useGroth) {
|
||||
throw std::ios_base::failure("Invalid Sprout proof for transaction format (expected PHGRProof, found GrothProof)");
|
||||
}
|
||||
::Serialize(s, proof);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Stream, typename T>
|
||||
inline void SerReadWriteSproutProof(Stream& s, const T& proof, bool useGroth, CSerActionSerialize ser_action)
|
||||
{
|
||||
auto ps = SproutProofSerializer<Stream>(s, useGroth);
|
||||
boost::apply_visitor(ps, proof);
|
||||
}
|
||||
|
||||
template<typename Stream, typename T>
|
||||
inline void SerReadWriteSproutProof(Stream& s, T& proof, bool useGroth, CSerActionUnserialize ser_action)
|
||||
{
|
||||
if (useGroth) {
|
||||
libzcash::GrothProof grothProof;
|
||||
::Unserialize(s, grothProof);
|
||||
proof = grothProof;
|
||||
} else {
|
||||
libzcash::PHGRProof pghrProof;
|
||||
::Unserialize(s, pghrProof);
|
||||
proof = pghrProof;
|
||||
}
|
||||
}
|
||||
|
||||
class JSDescription
|
||||
{
|
||||
public:
|
||||
@@ -47,14 +203,14 @@ public:
|
||||
// are derived from the secrets placed in the note
|
||||
// and the secret spend-authority key known by the
|
||||
// spender.
|
||||
boost::array<uint256, ZC_NUM_JS_INPUTS> nullifiers;
|
||||
std::array<uint256, ZC_NUM_JS_INPUTS> nullifiers;
|
||||
|
||||
// Note commitments are introduced into the commitment
|
||||
// tree, blinding the public about the values and
|
||||
// destinations involved in the JoinSplit. The presence of
|
||||
// a commitment in the note commitment tree is required
|
||||
// to spend it.
|
||||
boost::array<uint256, ZC_NUM_JS_OUTPUTS> commitments;
|
||||
std::array<uint256, ZC_NUM_JS_OUTPUTS> commitments;
|
||||
|
||||
// Ephemeral key
|
||||
uint256 ephemeralKey;
|
||||
@@ -63,7 +219,7 @@ public:
|
||||
// These contain trapdoors, values and other information
|
||||
// that the recipient needs, including a memo field. It
|
||||
// is encrypted using the scheme implemented in crypto/NoteEncryption.cpp
|
||||
boost::array<ZCNoteEncryption::Ciphertext, ZC_NUM_JS_OUTPUTS> ciphertexts = {{ {{0}} }};
|
||||
std::array<ZCNoteEncryption::Ciphertext, ZC_NUM_JS_OUTPUTS> ciphertexts = {{ {{0}} }};
|
||||
|
||||
// Random seed
|
||||
uint256 randomSeed;
|
||||
@@ -71,19 +227,21 @@ public:
|
||||
// MACs
|
||||
// The verification of the JoinSplit requires these MACs
|
||||
// to be provided as an input.
|
||||
boost::array<uint256, ZC_NUM_JS_INPUTS> macs;
|
||||
std::array<uint256, ZC_NUM_JS_INPUTS> macs;
|
||||
|
||||
// JoinSplit proof
|
||||
// This is a zk-SNARK which ensures that this JoinSplit is valid.
|
||||
libzcash::ZCProof proof;
|
||||
libzcash::SproutProof proof;
|
||||
|
||||
JSDescription(): vpub_old(0), vpub_new(0) { }
|
||||
|
||||
JSDescription(ZCJoinSplit& params,
|
||||
const uint256& pubKeyHash,
|
||||
JSDescription(
|
||||
bool makeGrothProof,
|
||||
ZCJoinSplit& params,
|
||||
const uint256& joinSplitPubKey,
|
||||
const uint256& rt,
|
||||
const boost::array<libzcash::JSInput, ZC_NUM_JS_INPUTS>& inputs,
|
||||
const boost::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS>& outputs,
|
||||
const std::array<libzcash::JSInput, ZC_NUM_JS_INPUTS>& inputs,
|
||||
const std::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS>& outputs,
|
||||
CAmount vpub_old,
|
||||
CAmount vpub_new,
|
||||
bool computeProof = true, // Set to false in some tests
|
||||
@@ -91,18 +249,14 @@ public:
|
||||
);
|
||||
|
||||
static JSDescription Randomized(
|
||||
bool makeGrothProof,
|
||||
ZCJoinSplit& params,
|
||||
const uint256& pubKeyHash,
|
||||
const uint256& joinSplitPubKey,
|
||||
const uint256& rt,
|
||||
boost::array<libzcash::JSInput, ZC_NUM_JS_INPUTS>& inputs,
|
||||
boost::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS>& outputs,
|
||||
#ifdef __LP64__
|
||||
boost::array<uint64_t, ZC_NUM_JS_INPUTS>& inputMap,
|
||||
boost::array<uint64_t, ZC_NUM_JS_OUTPUTS>& outputMap,
|
||||
#else
|
||||
boost::array<size_t, ZC_NUM_JS_INPUTS>& inputMap,
|
||||
boost::array<size_t, ZC_NUM_JS_OUTPUTS>& outputMap,
|
||||
#endif
|
||||
std::array<libzcash::JSInput, ZC_NUM_JS_INPUTS>& inputs,
|
||||
std::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS>& outputs,
|
||||
std::array<size_t, ZC_NUM_JS_INPUTS>& inputMap,
|
||||
std::array<size_t, ZC_NUM_JS_OUTPUTS>& outputMap,
|
||||
CAmount vpub_old,
|
||||
CAmount vpub_new,
|
||||
bool computeProof = true, // Set to false in some tests
|
||||
@@ -114,16 +268,22 @@ public:
|
||||
bool Verify(
|
||||
ZCJoinSplit& params,
|
||||
libzcash::ProofVerifier& verifier,
|
||||
const uint256& pubKeyHash
|
||||
const uint256& joinSplitPubKey
|
||||
) const;
|
||||
|
||||
// Returns the calculated h_sig
|
||||
uint256 h_sig(ZCJoinSplit& params, const uint256& pubKeyHash) const;
|
||||
uint256 h_sig(ZCJoinSplit& params, const uint256& joinSplitPubKey) const;
|
||||
|
||||
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) {
|
||||
// nVersion is set by CTransaction and CMutableTransaction to
|
||||
// (tx.fOverwintered << 31) | tx.nVersion
|
||||
bool fOverwintered = s.GetVersion() >> 31;
|
||||
int32_t txVersion = s.GetVersion() & 0x7FFFFFFF;
|
||||
bool useGroth = fOverwintered && txVersion >= SAPLING_TX_VERSION;
|
||||
|
||||
READWRITE(vpub_old);
|
||||
READWRITE(vpub_new);
|
||||
READWRITE(anchor);
|
||||
@@ -132,7 +292,7 @@ public:
|
||||
READWRITE(ephemeralKey);
|
||||
READWRITE(randomSeed);
|
||||
READWRITE(macs);
|
||||
READWRITE(proof);
|
||||
::SerReadWriteSproutProof(s, proof, useGroth, ser_action);
|
||||
READWRITE(ciphertexts);
|
||||
}
|
||||
|
||||
@@ -158,20 +318,19 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/** An outpoint - a combination of a transaction hash and an index n into its vout */
|
||||
class COutPoint
|
||||
class BaseOutPoint
|
||||
{
|
||||
public:
|
||||
uint256 hash;
|
||||
uint32_t n;
|
||||
|
||||
COutPoint() { SetNull(); }
|
||||
COutPoint(uint256 hashIn, uint32_t nIn) { hash = hashIn; n = nIn; }
|
||||
BaseOutPoint() { SetNull(); }
|
||||
BaseOutPoint(uint256 hashIn, uint32_t nIn) { hash = hashIn; n = nIn; }
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -179,21 +338,38 @@ public:
|
||||
void SetNull() { hash.SetNull(); n = (uint32_t) -1; }
|
||||
bool IsNull() const { return (hash.IsNull() && n == (uint32_t) -1); }
|
||||
|
||||
friend bool operator<(const COutPoint& a, const COutPoint& b)
|
||||
friend bool operator<(const BaseOutPoint& a, const BaseOutPoint& b)
|
||||
{
|
||||
return (a.hash < b.hash || (a.hash == b.hash && a.n < b.n));
|
||||
}
|
||||
|
||||
friend bool operator==(const COutPoint& a, const COutPoint& b)
|
||||
friend bool operator==(const BaseOutPoint& a, const BaseOutPoint& b)
|
||||
{
|
||||
return (a.hash == b.hash && a.n == b.n);
|
||||
}
|
||||
|
||||
friend bool operator!=(const COutPoint& a, const COutPoint& b)
|
||||
friend bool operator!=(const BaseOutPoint& a, const BaseOutPoint& b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
};
|
||||
|
||||
/** An outpoint - a combination of a transaction hash and an index n into its vout */
|
||||
class COutPoint : public BaseOutPoint
|
||||
{
|
||||
public:
|
||||
COutPoint() : BaseOutPoint() {};
|
||||
COutPoint(uint256 hashIn, uint32_t nIn) : BaseOutPoint(hashIn, nIn) {};
|
||||
std::string ToString() const;
|
||||
};
|
||||
|
||||
/** An outpoint - a combination of a transaction hash and an index n into its sapling
|
||||
* output description (vShieldedOutput) */
|
||||
class SaplingOutPoint : public BaseOutPoint
|
||||
{
|
||||
public:
|
||||
SaplingOutPoint() : BaseOutPoint() {};
|
||||
SaplingOutPoint(uint256 hashIn, uint32_t nIn) : BaseOutPoint(hashIn, nIn) {};
|
||||
std::string ToString() const;
|
||||
};
|
||||
|
||||
@@ -219,9 +395,9 @@ 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(scriptSig);
|
||||
READWRITE(*(CScriptBase*)(&scriptSig));
|
||||
READWRITE(nSequence);
|
||||
}
|
||||
|
||||
@@ -264,9 +440,9 @@ 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(scriptPubKey);
|
||||
READWRITE(*(CScriptBase*)(&scriptPubKey));
|
||||
}
|
||||
|
||||
void SetNull()
|
||||
@@ -295,7 +471,7 @@ public:
|
||||
if (scriptPubKey.IsUnspendable())
|
||||
return 0;
|
||||
|
||||
size_t nSize = GetSerializeSize(SER_DISK,0)+148u;
|
||||
size_t nSize = GetSerializeSize(*this, SER_DISK, 0) + 148u;
|
||||
return 3*minRelayTxFee.GetFee(nSize);
|
||||
}
|
||||
|
||||
@@ -321,6 +497,10 @@ public:
|
||||
static constexpr uint32_t OVERWINTER_VERSION_GROUP_ID = 0x03C48270;
|
||||
static_assert(OVERWINTER_VERSION_GROUP_ID != 0, "version group id must be non-zero as specified in ZIP 202");
|
||||
|
||||
// Sapling version group id
|
||||
static constexpr uint32_t SAPLING_VERSION_GROUP_ID = 0x892F2085;
|
||||
static_assert(SAPLING_VERSION_GROUP_ID != 0, "version group id must be non-zero as specified in ZIP 202");
|
||||
|
||||
struct CMutableTransaction;
|
||||
|
||||
/** The basic transaction that is broadcasted on the network and contained in
|
||||
@@ -340,13 +520,16 @@ protected:
|
||||
CTransaction(const CMutableTransaction &tx, bool evilDeveloperFlag);
|
||||
|
||||
public:
|
||||
typedef boost::array<unsigned char, 64> joinsplit_sig_t;
|
||||
typedef std::array<unsigned char, 64> joinsplit_sig_t;
|
||||
typedef std::array<unsigned char, 64> binding_sig_t;
|
||||
|
||||
// Transactions that include a list of JoinSplits are >= version 2.
|
||||
static const int32_t SPROUT_MIN_CURRENT_VERSION = 1;
|
||||
static const int32_t SPROUT_MAX_CURRENT_VERSION = 2;
|
||||
static const int32_t OVERWINTER_MIN_CURRENT_VERSION = 3;
|
||||
static const int32_t OVERWINTER_MAX_CURRENT_VERSION = 3;
|
||||
static const int32_t SAPLING_MIN_CURRENT_VERSION = 4;
|
||||
static const int32_t SAPLING_MAX_CURRENT_VERSION = 4;
|
||||
|
||||
static_assert(SPROUT_MIN_CURRENT_VERSION >= SPROUT_MIN_TX_VERSION,
|
||||
"standard rule for tx version should be consistent with network rule");
|
||||
@@ -358,6 +541,13 @@ public:
|
||||
OVERWINTER_MAX_CURRENT_VERSION >= OVERWINTER_MIN_CURRENT_VERSION),
|
||||
"standard rule for tx version should be consistent with network rule");
|
||||
|
||||
static_assert(SAPLING_MIN_CURRENT_VERSION >= SAPLING_MIN_TX_VERSION,
|
||||
"standard rule for tx version should be consistent with network rule");
|
||||
|
||||
static_assert( (SAPLING_MAX_CURRENT_VERSION <= SAPLING_MAX_TX_VERSION &&
|
||||
SAPLING_MAX_CURRENT_VERSION >= SAPLING_MIN_CURRENT_VERSION),
|
||||
"standard rule for tx version should be consistent with network rule");
|
||||
|
||||
// The local variables are made const to prevent unintended modification
|
||||
// without updating the cached hash value. However, CTransaction is not
|
||||
// actually immutable; deserialization and assignment are implemented,
|
||||
@@ -370,61 +560,82 @@ public:
|
||||
const std::vector<CTxOut> vout;
|
||||
const uint32_t nLockTime;
|
||||
const uint32_t nExpiryHeight;
|
||||
const CAmount valueBalance;
|
||||
const std::vector<SpendDescription> vShieldedSpend;
|
||||
const std::vector<OutputDescription> vShieldedOutput;
|
||||
const std::vector<JSDescription> vjoinsplit;
|
||||
const uint256 joinSplitPubKey;
|
||||
const joinsplit_sig_t joinSplitSig = {{0}};
|
||||
const binding_sig_t bindingSig = {{0}};
|
||||
|
||||
/** 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) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
uint32_t header;
|
||||
if (ser_action.ForRead()) {
|
||||
// When deserializing, unpack the 4 byte header to extract fOverwintered and nVersion.
|
||||
uint32_t header;
|
||||
READWRITE(header);
|
||||
*const_cast<bool*>(&fOverwintered) = header >> 31;
|
||||
*const_cast<int32_t*>(&this->nVersion) = header & 0x7FFFFFFF;
|
||||
} else {
|
||||
uint32_t header = GetHeader();
|
||||
header = GetHeader();
|
||||
READWRITE(header);
|
||||
}
|
||||
nVersion = this->nVersion;
|
||||
if (fOverwintered) {
|
||||
READWRITE(*const_cast<uint32_t*>(&this->nVersionGroupId));
|
||||
}
|
||||
|
||||
bool isOverwinterV3 = fOverwintered &&
|
||||
nVersionGroupId == OVERWINTER_VERSION_GROUP_ID &&
|
||||
nVersion == 3;
|
||||
if (fOverwintered && !isOverwinterV3) {
|
||||
bool isOverwinterV3 =
|
||||
fOverwintered &&
|
||||
nVersionGroupId == OVERWINTER_VERSION_GROUP_ID &&
|
||||
nVersion == OVERWINTER_TX_VERSION;
|
||||
bool isSaplingV4 =
|
||||
fOverwintered &&
|
||||
nVersionGroupId == SAPLING_VERSION_GROUP_ID &&
|
||||
nVersion == SAPLING_TX_VERSION;
|
||||
if (fOverwintered && !(isOverwinterV3 || isSaplingV4)) {
|
||||
throw std::ios_base::failure("Unknown transaction format");
|
||||
}
|
||||
|
||||
READWRITE(*const_cast<std::vector<CTxIn>*>(&vin));
|
||||
READWRITE(*const_cast<std::vector<CTxOut>*>(&vout));
|
||||
READWRITE(*const_cast<uint32_t*>(&nLockTime));
|
||||
if (isOverwinterV3) {
|
||||
if (isOverwinterV3 || isSaplingV4) {
|
||||
READWRITE(*const_cast<uint32_t*>(&nExpiryHeight));
|
||||
}
|
||||
if (isSaplingV4) {
|
||||
READWRITE(*const_cast<CAmount*>(&valueBalance));
|
||||
READWRITE(*const_cast<std::vector<SpendDescription>*>(&vShieldedSpend));
|
||||
READWRITE(*const_cast<std::vector<OutputDescription>*>(&vShieldedOutput));
|
||||
}
|
||||
if (nVersion >= 2) {
|
||||
READWRITE(*const_cast<std::vector<JSDescription>*>(&vjoinsplit));
|
||||
auto os = WithVersion(&s, static_cast<int>(header));
|
||||
::SerReadWrite(os, *const_cast<std::vector<JSDescription>*>(&vjoinsplit), ser_action);
|
||||
if (vjoinsplit.size() > 0) {
|
||||
READWRITE(*const_cast<uint256*>(&joinSplitPubKey));
|
||||
READWRITE(*const_cast<joinsplit_sig_t*>(&joinSplitSig));
|
||||
}
|
||||
}
|
||||
if (isSaplingV4 && !(vShieldedSpend.empty() && vShieldedOutput.empty())) {
|
||||
READWRITE(*const_cast<binding_sig_t*>(&bindingSig));
|
||||
}
|
||||
if (ser_action.ForRead())
|
||||
UpdateHash();
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
CTransaction(deserialize_type, Stream& s) : CTransaction(CMutableTransaction(deserialize, s)) {}
|
||||
|
||||
bool IsNull() const {
|
||||
return vin.empty() && vout.empty();
|
||||
}
|
||||
@@ -443,15 +654,24 @@ public:
|
||||
return header;
|
||||
}
|
||||
|
||||
// Return sum of txouts.
|
||||
/*
|
||||
* Context for the two methods below:
|
||||
* As at most one of vpub_new and vpub_old is non-zero in every JoinSplit,
|
||||
* we can think of a JoinSplit as an input or output according to which one
|
||||
* it is (e.g. if vpub_new is non-zero the joinSplit is "giving value" to
|
||||
* the outputs in the transaction). Similarly, we can think of the Sapling
|
||||
* shielded part of the transaction as an input or output according to
|
||||
* whether valueBalance - the sum of shielded input values minus the sum of
|
||||
* shielded output values - is positive or negative.
|
||||
*/
|
||||
|
||||
// Return sum of txouts, (negative valueBalance or zero) and JoinSplit vpub_old.
|
||||
CAmount GetValueOut() const;
|
||||
// GetValueIn() is a method on CCoinsViewCache, because
|
||||
// inputs must be known to compute value in.
|
||||
|
||||
// Return sum of JoinSplit vpub_new
|
||||
CAmount GetJoinSplitValueIn() const;
|
||||
// Return sum of JoinSplit vpub_old
|
||||
CAmount GetJoinSplitValueOut() const;
|
||||
// Return sum of (positive valueBalance or zero) and JoinSplit vpub_new
|
||||
CAmount GetShieldedValueIn() const;
|
||||
|
||||
// Compute priority, given priority of inputs and (optionally) tx size
|
||||
double ComputePriority(double dPriorityInputs, unsigned int nTxSize=0) const;
|
||||
@@ -535,9 +755,13 @@ struct CMutableTransaction
|
||||
std::vector<CTxOut> vout;
|
||||
uint32_t nLockTime;
|
||||
uint32_t nExpiryHeight;
|
||||
CAmount valueBalance;
|
||||
std::vector<SpendDescription> vShieldedSpend;
|
||||
std::vector<OutputDescription> vShieldedOutput;
|
||||
std::vector<JSDescription> vjoinsplit;
|
||||
uint256 joinSplitPubKey;
|
||||
CTransaction::joinsplit_sig_t joinSplitSig = {{0}};
|
||||
CTransaction::binding_sig_t bindingSig = {{0}};
|
||||
|
||||
CMutableTransaction();
|
||||
CMutableTransaction(const CTransaction& tx);
|
||||
@@ -545,47 +769,65 @@ struct CMutableTransaction
|
||||
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) {
|
||||
uint32_t header;
|
||||
if (ser_action.ForRead()) {
|
||||
// When deserializing, unpack the 4 byte header to extract fOverwintered and nVersion.
|
||||
uint32_t header;
|
||||
READWRITE(header);
|
||||
fOverwintered = header >> 31;
|
||||
this->nVersion = header & 0x7FFFFFFF;
|
||||
} else {
|
||||
// When serializing v1 and v2, the 4 byte header is nVersion
|
||||
uint32_t header = this->nVersion;
|
||||
header = this->nVersion;
|
||||
// When serializing Overwintered tx, the 4 byte header is the combination of fOverwintered and nVersion
|
||||
if (fOverwintered) {
|
||||
header |= 1 << 31;
|
||||
}
|
||||
READWRITE(header);
|
||||
}
|
||||
nVersion = this->nVersion;
|
||||
if (fOverwintered) {
|
||||
READWRITE(nVersionGroupId);
|
||||
}
|
||||
|
||||
bool isOverwinterV3 = fOverwintered &&
|
||||
nVersionGroupId == OVERWINTER_VERSION_GROUP_ID &&
|
||||
nVersion == 3;
|
||||
if (fOverwintered && !isOverwinterV3) {
|
||||
bool isOverwinterV3 =
|
||||
fOverwintered &&
|
||||
nVersionGroupId == OVERWINTER_VERSION_GROUP_ID &&
|
||||
nVersion == OVERWINTER_TX_VERSION;
|
||||
bool isSaplingV4 =
|
||||
fOverwintered &&
|
||||
nVersionGroupId == SAPLING_VERSION_GROUP_ID &&
|
||||
nVersion == SAPLING_TX_VERSION;
|
||||
if (fOverwintered && !(isOverwinterV3 || isSaplingV4)) {
|
||||
throw std::ios_base::failure("Unknown transaction format");
|
||||
}
|
||||
|
||||
READWRITE(vin);
|
||||
READWRITE(vout);
|
||||
READWRITE(nLockTime);
|
||||
if (isOverwinterV3) {
|
||||
if (isOverwinterV3 || isSaplingV4) {
|
||||
READWRITE(nExpiryHeight);
|
||||
}
|
||||
if (isSaplingV4) {
|
||||
READWRITE(valueBalance);
|
||||
READWRITE(vShieldedSpend);
|
||||
READWRITE(vShieldedOutput);
|
||||
}
|
||||
if (nVersion >= 2) {
|
||||
READWRITE(vjoinsplit);
|
||||
auto os = WithVersion(&s, static_cast<int>(header));
|
||||
::SerReadWrite(os, vjoinsplit, ser_action);
|
||||
if (vjoinsplit.size() > 0) {
|
||||
READWRITE(joinSplitPubKey);
|
||||
READWRITE(joinSplitSig);
|
||||
}
|
||||
}
|
||||
if (isSaplingV4 && !(vShieldedSpend.empty() && vShieldedOutput.empty())) {
|
||||
READWRITE(bindingSig);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
CMutableTransaction(deserialize_type, Stream& s) {
|
||||
Unserialize(s);
|
||||
}
|
||||
|
||||
/** Compute the hash of this CMutableTransaction. This is computed on the
|
||||
|
||||
Reference in New Issue
Block a user