Implement basic Sapling v4 transaction parser
Details of Sapling datatypes will be filled in later; for now, they are treated
as binary blobs.
Includes code cherry-picked from upstream commit:
7030d9eb47254499bba14f1c00abc6bf493efd91
BIP144: Serialization, hashes, relay (sender side)
This commit is contained in:
@@ -182,6 +182,9 @@ public:
|
|||||||
personal) == 0);
|
personal) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int GetType() const { return nType; }
|
||||||
|
int GetVersion() const { return nVersion; }
|
||||||
|
|
||||||
CBLAKE2bWriter& write(const char *pch, size_t size) {
|
CBLAKE2bWriter& write(const char *pch, size_t size) {
|
||||||
crypto_generichash_blake2b_update(&state, (const unsigned char*)pch, size);
|
crypto_generichash_blake2b_update(&state, (const unsigned char*)pch, size);
|
||||||
return (*this);
|
return (*this);
|
||||||
|
|||||||
@@ -72,23 +72,50 @@ JSDescription JSDescription::Randomized(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SproutProofVerifier : public boost::static_visitor<bool>
|
||||||
|
{
|
||||||
|
ZCJoinSplit& params;
|
||||||
|
libzcash::ProofVerifier& verifier;
|
||||||
|
const uint256& pubKeyHash;
|
||||||
|
const JSDescription& jsdesc;
|
||||||
|
|
||||||
|
public:
|
||||||
|
SproutProofVerifier(
|
||||||
|
ZCJoinSplit& params,
|
||||||
|
libzcash::ProofVerifier& verifier,
|
||||||
|
const uint256& pubKeyHash,
|
||||||
|
const JSDescription& jsdesc
|
||||||
|
) : params(params), jsdesc(jsdesc), verifier(verifier), pubKeyHash(pubKeyHash) {}
|
||||||
|
|
||||||
|
bool operator()(const libzcash::ZCProof& proof) const
|
||||||
|
{
|
||||||
|
return params.verify(
|
||||||
|
proof,
|
||||||
|
verifier,
|
||||||
|
pubKeyHash,
|
||||||
|
jsdesc.randomSeed,
|
||||||
|
jsdesc.macs,
|
||||||
|
jsdesc.nullifiers,
|
||||||
|
jsdesc.commitments,
|
||||||
|
jsdesc.vpub_old,
|
||||||
|
jsdesc.vpub_new,
|
||||||
|
jsdesc.anchor
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(const libzcash::GrothProof& proof) const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
bool JSDescription::Verify(
|
bool JSDescription::Verify(
|
||||||
ZCJoinSplit& params,
|
ZCJoinSplit& params,
|
||||||
libzcash::ProofVerifier& verifier,
|
libzcash::ProofVerifier& verifier,
|
||||||
const uint256& pubKeyHash
|
const uint256& pubKeyHash
|
||||||
) const {
|
) const {
|
||||||
return params.verify(
|
auto pv = SproutProofVerifier(params, verifier, pubKeyHash, *this);
|
||||||
proof,
|
boost::apply_visitor(pv, proof);
|
||||||
verifier,
|
|
||||||
pubKeyHash,
|
|
||||||
randomSeed,
|
|
||||||
macs,
|
|
||||||
nullifiers,
|
|
||||||
commitments,
|
|
||||||
vpub_old,
|
|
||||||
vpub_new,
|
|
||||||
anchor
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint256 JSDescription::h_sig(ZCJoinSplit& params, const uint256& pubKeyHash) const
|
uint256 JSDescription::h_sig(ZCJoinSplit& params, const uint256& pubKeyHash) const
|
||||||
@@ -146,10 +173,12 @@ std::string CTxOut::ToString() const
|
|||||||
return strprintf("CTxOut(nValue=%d.%08d, scriptPubKey=%s)", nValue / COIN, nValue % COIN, HexStr(scriptPubKey).substr(0, 30));
|
return strprintf("CTxOut(nValue=%d.%08d, scriptPubKey=%s)", nValue / COIN, nValue % COIN, HexStr(scriptPubKey).substr(0, 30));
|
||||||
}
|
}
|
||||||
|
|
||||||
CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::SPROUT_MIN_CURRENT_VERSION), fOverwintered(false), nVersionGroupId(0), nExpiryHeight(0), nLockTime(0) {}
|
CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::SPROUT_MIN_CURRENT_VERSION), fOverwintered(false), nVersionGroupId(0), nExpiryHeight(0), nLockTime(0), valueBalance(0) {}
|
||||||
CMutableTransaction::CMutableTransaction(const CTransaction& tx) : nVersion(tx.nVersion), fOverwintered(tx.fOverwintered), nVersionGroupId(tx.nVersionGroupId), nExpiryHeight(tx.nExpiryHeight),
|
CMutableTransaction::CMutableTransaction(const CTransaction& tx) : nVersion(tx.nVersion), fOverwintered(tx.fOverwintered), nVersionGroupId(tx.nVersionGroupId), nExpiryHeight(tx.nExpiryHeight),
|
||||||
vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime),
|
vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime),
|
||||||
vjoinsplit(tx.vjoinsplit), joinSplitPubKey(tx.joinSplitPubKey), joinSplitSig(tx.joinSplitSig)
|
valueBalance(tx.valueBalance), vShieldedSpend(tx.vShieldedSpend), vShieldedOutput(tx.vShieldedOutput),
|
||||||
|
vjoinsplit(tx.vjoinsplit), joinSplitPubKey(tx.joinSplitPubKey), joinSplitSig(tx.joinSplitSig),
|
||||||
|
bindingSig(tx.bindingSig)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -164,11 +193,13 @@ void CTransaction::UpdateHash() const
|
|||||||
*const_cast<uint256*>(&hash) = SerializeHash(*this);
|
*const_cast<uint256*>(&hash) = SerializeHash(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
CTransaction::CTransaction() : nVersion(CTransaction::SPROUT_MIN_CURRENT_VERSION), fOverwintered(false), nVersionGroupId(0), nExpiryHeight(0), vin(), vout(), nLockTime(0), vjoinsplit(), joinSplitPubKey(), joinSplitSig() { }
|
CTransaction::CTransaction() : nVersion(CTransaction::SPROUT_MIN_CURRENT_VERSION), fOverwintered(false), nVersionGroupId(0), nExpiryHeight(0), vin(), vout(), nLockTime(0), valueBalance(0), vShieldedSpend(), vShieldedOutput(), vjoinsplit(), joinSplitPubKey(), joinSplitSig(), bindingSig() { }
|
||||||
|
|
||||||
CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), fOverwintered(tx.fOverwintered), nVersionGroupId(tx.nVersionGroupId), nExpiryHeight(tx.nExpiryHeight),
|
CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), fOverwintered(tx.fOverwintered), nVersionGroupId(tx.nVersionGroupId), nExpiryHeight(tx.nExpiryHeight),
|
||||||
vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime),
|
vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime),
|
||||||
vjoinsplit(tx.vjoinsplit), joinSplitPubKey(tx.joinSplitPubKey), joinSplitSig(tx.joinSplitSig)
|
valueBalance(tx.valueBalance), vShieldedSpend(tx.vShieldedSpend), vShieldedOutput(tx.vShieldedOutput),
|
||||||
|
vjoinsplit(tx.vjoinsplit), joinSplitPubKey(tx.joinSplitPubKey), joinSplitSig(tx.joinSplitSig),
|
||||||
|
bindingSig(tx.bindingSig)
|
||||||
{
|
{
|
||||||
UpdateHash();
|
UpdateHash();
|
||||||
}
|
}
|
||||||
@@ -179,13 +210,17 @@ CTransaction::CTransaction(
|
|||||||
const CMutableTransaction &tx,
|
const CMutableTransaction &tx,
|
||||||
bool evilDeveloperFlag) : nVersion(tx.nVersion), fOverwintered(tx.fOverwintered), nVersionGroupId(tx.nVersionGroupId), nExpiryHeight(tx.nExpiryHeight),
|
bool evilDeveloperFlag) : nVersion(tx.nVersion), fOverwintered(tx.fOverwintered), nVersionGroupId(tx.nVersionGroupId), nExpiryHeight(tx.nExpiryHeight),
|
||||||
vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime),
|
vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime),
|
||||||
vjoinsplit(tx.vjoinsplit), joinSplitPubKey(tx.joinSplitPubKey), joinSplitSig(tx.joinSplitSig)
|
valueBalance(tx.valueBalance), vShieldedSpend(tx.vShieldedSpend), vShieldedOutput(tx.vShieldedOutput),
|
||||||
|
vjoinsplit(tx.vjoinsplit), joinSplitPubKey(tx.joinSplitPubKey), joinSplitSig(tx.joinSplitSig),
|
||||||
|
bindingSig(tx.bindingSig)
|
||||||
{
|
{
|
||||||
assert(evilDeveloperFlag);
|
assert(evilDeveloperFlag);
|
||||||
}
|
}
|
||||||
|
|
||||||
CTransaction::CTransaction(CMutableTransaction &&tx) : nVersion(tx.nVersion), fOverwintered(tx.fOverwintered), nVersionGroupId(tx.nVersionGroupId),
|
CTransaction::CTransaction(CMutableTransaction &&tx) : nVersion(tx.nVersion), fOverwintered(tx.fOverwintered), nVersionGroupId(tx.nVersionGroupId),
|
||||||
vin(std::move(tx.vin)), vout(std::move(tx.vout)), nLockTime(tx.nLockTime), nExpiryHeight(tx.nExpiryHeight),
|
vin(std::move(tx.vin)), vout(std::move(tx.vout)), nLockTime(tx.nLockTime), nExpiryHeight(tx.nExpiryHeight),
|
||||||
|
valueBalance(tx.valueBalance),
|
||||||
|
vShieldedSpend(std::move(tx.vShieldedSpend)), vShieldedOutput(std::move(tx.vShieldedOutput)),
|
||||||
vjoinsplit(std::move(tx.vjoinsplit)),
|
vjoinsplit(std::move(tx.vjoinsplit)),
|
||||||
joinSplitPubKey(std::move(tx.joinSplitPubKey)), joinSplitSig(std::move(tx.joinSplitSig))
|
joinSplitPubKey(std::move(tx.joinSplitPubKey)), joinSplitSig(std::move(tx.joinSplitSig))
|
||||||
{
|
{
|
||||||
@@ -200,9 +235,13 @@ CTransaction& CTransaction::operator=(const CTransaction &tx) {
|
|||||||
*const_cast<std::vector<CTxOut>*>(&vout) = tx.vout;
|
*const_cast<std::vector<CTxOut>*>(&vout) = tx.vout;
|
||||||
*const_cast<unsigned int*>(&nLockTime) = tx.nLockTime;
|
*const_cast<unsigned int*>(&nLockTime) = tx.nLockTime;
|
||||||
*const_cast<uint32_t*>(&nExpiryHeight) = tx.nExpiryHeight;
|
*const_cast<uint32_t*>(&nExpiryHeight) = tx.nExpiryHeight;
|
||||||
|
*const_cast<CAmount*>(&valueBalance) = tx.valueBalance;
|
||||||
|
*const_cast<std::vector<SpendDescription>*>(&vShieldedSpend) = tx.vShieldedSpend;
|
||||||
|
*const_cast<std::vector<OutputDescription>*>(&vShieldedOutput) = tx.vShieldedOutput;
|
||||||
*const_cast<std::vector<JSDescription>*>(&vjoinsplit) = tx.vjoinsplit;
|
*const_cast<std::vector<JSDescription>*>(&vjoinsplit) = tx.vjoinsplit;
|
||||||
*const_cast<uint256*>(&joinSplitPubKey) = tx.joinSplitPubKey;
|
*const_cast<uint256*>(&joinSplitPubKey) = tx.joinSplitPubKey;
|
||||||
*const_cast<joinsplit_sig_t*>(&joinSplitSig) = tx.joinSplitSig;
|
*const_cast<joinsplit_sig_t*>(&joinSplitSig) = tx.joinSplitSig;
|
||||||
|
*const_cast<binding_sig_t*>(&bindingSig) = tx.bindingSig;
|
||||||
*const_cast<uint256*>(&hash) = tx.hash;
|
*const_cast<uint256*>(&hash) = tx.hash;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@@ -279,6 +318,19 @@ std::string CTransaction::ToString() const
|
|||||||
vin.size(),
|
vin.size(),
|
||||||
vout.size(),
|
vout.size(),
|
||||||
nLockTime);
|
nLockTime);
|
||||||
|
} else if (nVersion >= SAPLING_MIN_TX_VERSION) {
|
||||||
|
str += strprintf("CTransaction(hash=%s, ver=%d, fOverwintered=%d, nVersionGroupId=%08x, vin.size=%u, vout.size=%u, nLockTime=%u, nExpiryHeight=%u, valueBalance=%u, vShieldedSpend.size=%u, vShieldedOutput.size=%u)\n",
|
||||||
|
GetHash().ToString().substr(0,10),
|
||||||
|
nVersion,
|
||||||
|
fOverwintered,
|
||||||
|
nVersionGroupId,
|
||||||
|
vin.size(),
|
||||||
|
vout.size(),
|
||||||
|
nLockTime,
|
||||||
|
nExpiryHeight,
|
||||||
|
valueBalance,
|
||||||
|
vShieldedSpend.size(),
|
||||||
|
vShieldedOutput.size());
|
||||||
} else if (nVersion >= 3) {
|
} else if (nVersion >= 3) {
|
||||||
str += strprintf("CTransaction(hash=%s, ver=%d, fOverwintered=%d, nVersionGroupId=%08x, vin.size=%u, vout.size=%u, nLockTime=%u, nExpiryHeight=%u)\n",
|
str += strprintf("CTransaction(hash=%s, ver=%d, fOverwintered=%d, nVersionGroupId=%08x, vin.size=%u, vout.size=%u, nLockTime=%u, nExpiryHeight=%u)\n",
|
||||||
GetHash().ToString().substr(0,10),
|
GetHash().ToString().substr(0,10),
|
||||||
|
|||||||
@@ -10,16 +10,97 @@
|
|||||||
#include "random.h"
|
#include "random.h"
|
||||||
#include "script/script.h"
|
#include "script/script.h"
|
||||||
#include "serialize.h"
|
#include "serialize.h"
|
||||||
|
#include "streams.h"
|
||||||
#include "uint256.h"
|
#include "uint256.h"
|
||||||
#include "consensus/consensus.h"
|
#include "consensus/consensus.h"
|
||||||
|
|
||||||
#include <boost/array.hpp>
|
#include <boost/array.hpp>
|
||||||
|
#include <boost/variant.hpp>
|
||||||
|
|
||||||
#include "zcash/NoteEncryption.hpp"
|
#include "zcash/NoteEncryption.hpp"
|
||||||
#include "zcash/Zcash.h"
|
#include "zcash/Zcash.h"
|
||||||
#include "zcash/JoinSplit.hpp"
|
#include "zcash/JoinSplit.hpp"
|
||||||
#include "zcash/Proof.hpp"
|
#include "zcash/Proof.hpp"
|
||||||
|
|
||||||
|
// 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");
|
||||||
|
|
||||||
|
static constexpr size_t GROTH_PROOF_SIZE = (
|
||||||
|
48 + // π_A
|
||||||
|
96 + // π_B
|
||||||
|
48); // π_C
|
||||||
|
static constexpr size_t SPEND_DESCRIPTION_SIZE = (
|
||||||
|
32 + // cv
|
||||||
|
32 + // anchor
|
||||||
|
32 + // nullifier
|
||||||
|
32 + // rk
|
||||||
|
GROTH_PROOF_SIZE +
|
||||||
|
64); // spendAuthSig
|
||||||
|
static constexpr size_t OUTPUT_DESCRIPTION_SIZE = (
|
||||||
|
32 + // cv
|
||||||
|
32 + // cm
|
||||||
|
32 + // ephemeralKey
|
||||||
|
580 + // encCiphertext
|
||||||
|
80 + // outCiphertext
|
||||||
|
GROTH_PROOF_SIZE);
|
||||||
|
|
||||||
|
namespace libzcash {
|
||||||
|
typedef boost::array<unsigned char, GROTH_PROOF_SIZE> GrothProof;
|
||||||
|
}
|
||||||
|
typedef boost::array<unsigned char, SPEND_DESCRIPTION_SIZE> SpendDescription;
|
||||||
|
typedef boost::array<unsigned char, OUTPUT_DESCRIPTION_SIZE> OutputDescription;
|
||||||
|
|
||||||
|
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::ZCProof& 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::ZCProof pghrProof;
|
||||||
|
::Unserialize(s, pghrProof);
|
||||||
|
proof = pghrProof;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class JSDescription
|
class JSDescription
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -66,7 +147,7 @@ public:
|
|||||||
|
|
||||||
// JoinSplit proof
|
// JoinSplit proof
|
||||||
// This is a zk-SNARK which ensures that this JoinSplit is valid.
|
// This is a zk-SNARK which ensures that this JoinSplit is valid.
|
||||||
libzcash::ZCProof proof;
|
boost::variant<libzcash::ZCProof, libzcash::GrothProof> proof;
|
||||||
|
|
||||||
JSDescription(): vpub_old(0), vpub_new(0) { }
|
JSDescription(): vpub_old(0), vpub_new(0) { }
|
||||||
|
|
||||||
@@ -110,6 +191,12 @@ public:
|
|||||||
|
|
||||||
template <typename Stream, typename Operation>
|
template <typename Stream, typename Operation>
|
||||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
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_old);
|
||||||
READWRITE(vpub_new);
|
READWRITE(vpub_new);
|
||||||
READWRITE(anchor);
|
READWRITE(anchor);
|
||||||
@@ -118,7 +205,7 @@ public:
|
|||||||
READWRITE(ephemeralKey);
|
READWRITE(ephemeralKey);
|
||||||
READWRITE(randomSeed);
|
READWRITE(randomSeed);
|
||||||
READWRITE(macs);
|
READWRITE(macs);
|
||||||
READWRITE(proof);
|
::SerReadWriteSproutProof(s, proof, useGroth, ser_action);
|
||||||
READWRITE(ciphertexts);
|
READWRITE(ciphertexts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,6 +419,7 @@ protected:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
typedef boost::array<unsigned char, 64> joinsplit_sig_t;
|
typedef boost::array<unsigned char, 64> joinsplit_sig_t;
|
||||||
|
typedef boost::array<unsigned char, 64> binding_sig_t;
|
||||||
|
|
||||||
// Transactions that include a list of JoinSplits are >= version 2.
|
// Transactions that include a list of JoinSplits are >= version 2.
|
||||||
static const int32_t SPROUT_MIN_CURRENT_VERSION = 1;
|
static const int32_t SPROUT_MIN_CURRENT_VERSION = 1;
|
||||||
@@ -361,9 +449,13 @@ public:
|
|||||||
const std::vector<CTxOut> vout;
|
const std::vector<CTxOut> vout;
|
||||||
const uint32_t nLockTime;
|
const uint32_t nLockTime;
|
||||||
const uint32_t nExpiryHeight;
|
const uint32_t nExpiryHeight;
|
||||||
|
const CAmount valueBalance;
|
||||||
|
const std::vector<SpendDescription> vShieldedSpend;
|
||||||
|
const std::vector<OutputDescription> vShieldedOutput;
|
||||||
const std::vector<JSDescription> vjoinsplit;
|
const std::vector<JSDescription> vjoinsplit;
|
||||||
const uint256 joinSplitPubKey;
|
const uint256 joinSplitPubKey;
|
||||||
const joinsplit_sig_t joinSplitSig = {{0}};
|
const joinsplit_sig_t joinSplitSig = {{0}};
|
||||||
|
const binding_sig_t bindingSig = {{0}};
|
||||||
|
|
||||||
/** Construct a CTransaction that qualifies as IsNull() */
|
/** Construct a CTransaction that qualifies as IsNull() */
|
||||||
CTransaction();
|
CTransaction();
|
||||||
@@ -378,14 +470,14 @@ public:
|
|||||||
|
|
||||||
template <typename Stream, typename Operation>
|
template <typename Stream, typename Operation>
|
||||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||||
|
uint32_t header;
|
||||||
if (ser_action.ForRead()) {
|
if (ser_action.ForRead()) {
|
||||||
// When deserializing, unpack the 4 byte header to extract fOverwintered and nVersion.
|
// When deserializing, unpack the 4 byte header to extract fOverwintered and nVersion.
|
||||||
uint32_t header;
|
|
||||||
READWRITE(header);
|
READWRITE(header);
|
||||||
*const_cast<bool*>(&fOverwintered) = header >> 31;
|
*const_cast<bool*>(&fOverwintered) = header >> 31;
|
||||||
*const_cast<int32_t*>(&this->nVersion) = header & 0x7FFFFFFF;
|
*const_cast<int32_t*>(&this->nVersion) = header & 0x7FFFFFFF;
|
||||||
} else {
|
} else {
|
||||||
uint32_t header = GetHeader();
|
header = GetHeader();
|
||||||
READWRITE(header);
|
READWRITE(header);
|
||||||
}
|
}
|
||||||
if (fOverwintered) {
|
if (fOverwintered) {
|
||||||
@@ -395,23 +487,36 @@ public:
|
|||||||
bool isOverwinterV3 = fOverwintered &&
|
bool isOverwinterV3 = fOverwintered &&
|
||||||
nVersionGroupId == OVERWINTER_VERSION_GROUP_ID &&
|
nVersionGroupId == OVERWINTER_VERSION_GROUP_ID &&
|
||||||
nVersion == 3;
|
nVersion == 3;
|
||||||
if (fOverwintered && !isOverwinterV3) {
|
bool isSaplingV4 =
|
||||||
|
fOverwintered &&
|
||||||
|
nVersionGroupId == SAPLING_VERSION_GROUP_ID &&
|
||||||
|
nVersion == SAPLING_TX_VERSION;
|
||||||
|
if (fOverwintered && !(isOverwinterV3 || isSaplingV4)) {
|
||||||
throw std::ios_base::failure("Unknown transaction format");
|
throw std::ios_base::failure("Unknown transaction format");
|
||||||
}
|
}
|
||||||
|
|
||||||
READWRITE(*const_cast<std::vector<CTxIn>*>(&vin));
|
READWRITE(*const_cast<std::vector<CTxIn>*>(&vin));
|
||||||
READWRITE(*const_cast<std::vector<CTxOut>*>(&vout));
|
READWRITE(*const_cast<std::vector<CTxOut>*>(&vout));
|
||||||
READWRITE(*const_cast<uint32_t*>(&nLockTime));
|
READWRITE(*const_cast<uint32_t*>(&nLockTime));
|
||||||
if (isOverwinterV3) {
|
if (isOverwinterV3 || isSaplingV4) {
|
||||||
READWRITE(*const_cast<uint32_t*>(&nExpiryHeight));
|
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) {
|
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) {
|
if (vjoinsplit.size() > 0) {
|
||||||
READWRITE(*const_cast<uint256*>(&joinSplitPubKey));
|
READWRITE(*const_cast<uint256*>(&joinSplitPubKey));
|
||||||
READWRITE(*const_cast<joinsplit_sig_t*>(&joinSplitSig));
|
READWRITE(*const_cast<joinsplit_sig_t*>(&joinSplitSig));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (isSaplingV4 && !(vShieldedSpend.empty() && vShieldedOutput.empty())) {
|
||||||
|
READWRITE(*const_cast<binding_sig_t*>(&bindingSig));
|
||||||
|
}
|
||||||
if (ser_action.ForRead())
|
if (ser_action.ForRead())
|
||||||
UpdateHash();
|
UpdateHash();
|
||||||
}
|
}
|
||||||
@@ -479,9 +584,13 @@ struct CMutableTransaction
|
|||||||
std::vector<CTxOut> vout;
|
std::vector<CTxOut> vout;
|
||||||
uint32_t nLockTime;
|
uint32_t nLockTime;
|
||||||
uint32_t nExpiryHeight;
|
uint32_t nExpiryHeight;
|
||||||
|
CAmount valueBalance;
|
||||||
|
std::vector<SpendDescription> vShieldedSpend;
|
||||||
|
std::vector<OutputDescription> vShieldedOutput;
|
||||||
std::vector<JSDescription> vjoinsplit;
|
std::vector<JSDescription> vjoinsplit;
|
||||||
uint256 joinSplitPubKey;
|
uint256 joinSplitPubKey;
|
||||||
CTransaction::joinsplit_sig_t joinSplitSig = {{0}};
|
CTransaction::joinsplit_sig_t joinSplitSig = {{0}};
|
||||||
|
CTransaction::binding_sig_t bindingSig = {{0}};
|
||||||
|
|
||||||
CMutableTransaction();
|
CMutableTransaction();
|
||||||
CMutableTransaction(const CTransaction& tx);
|
CMutableTransaction(const CTransaction& tx);
|
||||||
@@ -490,15 +599,15 @@ struct CMutableTransaction
|
|||||||
|
|
||||||
template <typename Stream, typename Operation>
|
template <typename Stream, typename Operation>
|
||||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||||
|
uint32_t header;
|
||||||
if (ser_action.ForRead()) {
|
if (ser_action.ForRead()) {
|
||||||
// When deserializing, unpack the 4 byte header to extract fOverwintered and nVersion.
|
// When deserializing, unpack the 4 byte header to extract fOverwintered and nVersion.
|
||||||
uint32_t header;
|
|
||||||
READWRITE(header);
|
READWRITE(header);
|
||||||
fOverwintered = header >> 31;
|
fOverwintered = header >> 31;
|
||||||
this->nVersion = header & 0x7FFFFFFF;
|
this->nVersion = header & 0x7FFFFFFF;
|
||||||
} else {
|
} else {
|
||||||
// When serializing v1 and v2, the 4 byte header is nVersion
|
// 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
|
// When serializing Overwintered tx, the 4 byte header is the combination of fOverwintered and nVersion
|
||||||
if (fOverwintered) {
|
if (fOverwintered) {
|
||||||
header |= 1 << 31;
|
header |= 1 << 31;
|
||||||
@@ -512,23 +621,36 @@ struct CMutableTransaction
|
|||||||
bool isOverwinterV3 = fOverwintered &&
|
bool isOverwinterV3 = fOverwintered &&
|
||||||
nVersionGroupId == OVERWINTER_VERSION_GROUP_ID &&
|
nVersionGroupId == OVERWINTER_VERSION_GROUP_ID &&
|
||||||
nVersion == 3;
|
nVersion == 3;
|
||||||
if (fOverwintered && !isOverwinterV3) {
|
bool isSaplingV4 =
|
||||||
|
fOverwintered &&
|
||||||
|
nVersionGroupId == SAPLING_VERSION_GROUP_ID &&
|
||||||
|
nVersion == SAPLING_TX_VERSION;
|
||||||
|
if (fOverwintered && !(isOverwinterV3 || isSaplingV4)) {
|
||||||
throw std::ios_base::failure("Unknown transaction format");
|
throw std::ios_base::failure("Unknown transaction format");
|
||||||
}
|
}
|
||||||
|
|
||||||
READWRITE(vin);
|
READWRITE(vin);
|
||||||
READWRITE(vout);
|
READWRITE(vout);
|
||||||
READWRITE(nLockTime);
|
READWRITE(nLockTime);
|
||||||
if (isOverwinterV3) {
|
if (isOverwinterV3 || isSaplingV4) {
|
||||||
READWRITE(nExpiryHeight);
|
READWRITE(nExpiryHeight);
|
||||||
}
|
}
|
||||||
|
if (isSaplingV4) {
|
||||||
|
READWRITE(valueBalance);
|
||||||
|
READWRITE(vShieldedSpend);
|
||||||
|
READWRITE(vShieldedOutput);
|
||||||
|
}
|
||||||
if (nVersion >= 2) {
|
if (nVersion >= 2) {
|
||||||
READWRITE(vjoinsplit);
|
auto os = WithVersion(&s, static_cast<int>(header));
|
||||||
|
::SerReadWrite(os, vjoinsplit, ser_action);
|
||||||
if (vjoinsplit.size() > 0) {
|
if (vjoinsplit.size() > 0) {
|
||||||
READWRITE(joinSplitPubKey);
|
READWRITE(joinSplitPubKey);
|
||||||
READWRITE(joinSplitSig);
|
READWRITE(joinSplitSig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (isSaplingV4 && !(vShieldedSpend.empty() && vShieldedOutput.empty())) {
|
||||||
|
READWRITE(bindingSig);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Stream>
|
template <typename Stream>
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fInclud
|
|||||||
|
|
||||||
|
|
||||||
UniValue TxJoinSplitToJSON(const CTransaction& tx) {
|
UniValue TxJoinSplitToJSON(const CTransaction& tx) {
|
||||||
|
bool useGroth = tx.fOverwintered && tx.nVersion >= SAPLING_TX_VERSION;
|
||||||
UniValue vjoinsplit(UniValue::VARR);
|
UniValue vjoinsplit(UniValue::VARR);
|
||||||
for (unsigned int i = 0; i < tx.vjoinsplit.size(); i++) {
|
for (unsigned int i = 0; i < tx.vjoinsplit.size(); i++) {
|
||||||
const JSDescription& jsdescription = tx.vjoinsplit[i];
|
const JSDescription& jsdescription = tx.vjoinsplit[i];
|
||||||
@@ -95,7 +96,8 @@ UniValue TxJoinSplitToJSON(const CTransaction& tx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
CDataStream ssProof(SER_NETWORK, PROTOCOL_VERSION);
|
CDataStream ssProof(SER_NETWORK, PROTOCOL_VERSION);
|
||||||
ssProof << jsdescription.proof;
|
auto ps = SproutProofSerializer<CDataStream>(ssProof, useGroth);
|
||||||
|
boost::apply_visitor(ps, jsdescription.proof);
|
||||||
joinsplit.push_back(Pair("proof", HexStr(ssProof.begin(), ssProof.end())));
|
joinsplit.push_back(Pair("proof", HexStr(ssProof.begin(), ssProof.end())));
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1165,7 +1165,7 @@ uint256 SignatureHash(
|
|||||||
memcpy(personalization, "ZcashSigHash", 12);
|
memcpy(personalization, "ZcashSigHash", 12);
|
||||||
memcpy(personalization+12, &leConsensusBranchId, 4);
|
memcpy(personalization+12, &leConsensusBranchId, 4);
|
||||||
|
|
||||||
CBLAKE2bWriter ss(SER_GETHASH, 0, personalization);
|
CBLAKE2bWriter ss(SER_GETHASH, static_cast<int>(txTo.GetHeader()), personalization);
|
||||||
// Header
|
// Header
|
||||||
ss << txTo.GetHeader();
|
ss << txTo.GetHeader();
|
||||||
// Version group ID
|
// Version group ID
|
||||||
|
|||||||
@@ -22,6 +22,53 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
template<typename Stream>
|
||||||
|
class OverrideStream
|
||||||
|
{
|
||||||
|
Stream* stream;
|
||||||
|
|
||||||
|
const int nType;
|
||||||
|
const int nVersion;
|
||||||
|
|
||||||
|
public:
|
||||||
|
OverrideStream(Stream* stream_, int nType_, int nVersion_) : stream(stream_), nType(nType_), nVersion(nVersion_) {}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
OverrideStream<Stream>& operator<<(const T& obj)
|
||||||
|
{
|
||||||
|
// Serialize to this stream
|
||||||
|
::Serialize(*this, obj);
|
||||||
|
return (*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
OverrideStream<Stream>& operator>>(T&& obj)
|
||||||
|
{
|
||||||
|
// Unserialize from this stream
|
||||||
|
::Unserialize(*this, obj);
|
||||||
|
return (*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(const char* pch, size_t nSize)
|
||||||
|
{
|
||||||
|
stream->write(pch, nSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
void read(char* pch, size_t nSize)
|
||||||
|
{
|
||||||
|
stream->read(pch, nSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetVersion() const { return nVersion; }
|
||||||
|
int GetType() const { return nType; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename S>
|
||||||
|
OverrideStream<S> WithVersion(S* s, int nVersion)
|
||||||
|
{
|
||||||
|
return OverrideStream<S>(s, s->GetType(), nVersion);
|
||||||
|
}
|
||||||
|
|
||||||
/** Double ended buffer combining vector and stream-like interfaces.
|
/** Double ended buffer combining vector and stream-like interfaces.
|
||||||
*
|
*
|
||||||
* >> and << read and write unformatted data using the above serialization templates.
|
* >> and << read and write unformatted data using the above serialization templates.
|
||||||
|
|||||||
Reference in New Issue
Block a user