Introduce `vpour` to `CTransaction`.
Transactions of version 2 and above contain a `vpour` field which is a vector of `CPourTx` objects that embody our protocol. We introduce serialization primitives for boost::array (we intend for changing the amount of inputs and outputs in the circuit to be simple). SIGHASH_* operations hash this field like any other for now.
This commit is contained in:
parent
74e519aa2d
commit
5884044ba9
|
@ -60,7 +60,7 @@ 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), nLockTime(tx.nLockTime), vpour(tx.vpour) {}
|
||||
|
||||
uint256 CMutableTransaction::GetHash() const
|
||||
{
|
||||
|
@ -72,9 +72,9 @@ void CTransaction::UpdateHash() const
|
|||
*const_cast<uint256*>(&hash) = SerializeHash(*this);
|
||||
}
|
||||
|
||||
CTransaction::CTransaction() : nVersion(CTransaction::CURRENT_VERSION), vin(), vout(), nLockTime(0) { }
|
||||
CTransaction::CTransaction() : nVersion(CTransaction::CURRENT_VERSION), vin(), vout(), nLockTime(0), vpour() { }
|
||||
|
||||
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), nLockTime(tx.nLockTime), vpour(tx.vpour) {
|
||||
UpdateHash();
|
||||
}
|
||||
|
||||
|
@ -83,6 +83,7 @@ CTransaction& CTransaction::operator=(const CTransaction &tx) {
|
|||
*const_cast<std::vector<CTxIn>*>(&vin) = tx.vin;
|
||||
*const_cast<std::vector<CTxOut>*>(&vout) = tx.vout;
|
||||
*const_cast<unsigned int*>(&nLockTime) = tx.nLockTime;
|
||||
*const_cast<std::vector<CPourTx>*>(&vpour) = tx.vpour;
|
||||
*const_cast<uint256*>(&hash) = tx.hash;
|
||||
return *this;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,100 @@
|
|||
#include "serialize.h"
|
||||
#include "uint256.h"
|
||||
|
||||
#include <boost/array.hpp>
|
||||
|
||||
class CPourTx
|
||||
{
|
||||
public:
|
||||
// These values 'enter from' and 'exit to' the value
|
||||
// pool, respectively.
|
||||
CAmount vpub_old;
|
||||
CAmount vpub_new;
|
||||
|
||||
// These scripts are used to bind a Pour to the outer
|
||||
// transaction it is placed in. The Pour will
|
||||
// authenticate the hash of the scriptPubKey, and the
|
||||
// provided scriptSig with be appended during
|
||||
// transaction verification.
|
||||
CScript scriptPubKey;
|
||||
CScript scriptSig;
|
||||
|
||||
// Pours are always anchored to a root in the bucket
|
||||
// commitment tree at some point in the blockchain
|
||||
// history or in the history of the current
|
||||
// transaction.
|
||||
uint256 anchor;
|
||||
|
||||
// Serials are used to prevent double-spends. They
|
||||
// are derived from the secrets placed in the bucket
|
||||
// and the secret spend-authority key known by the
|
||||
// spender.
|
||||
boost::array<uint256, 2> serials;
|
||||
|
||||
// Bucket commitments are introduced into the commitment
|
||||
// tree, blinding the public about the values and
|
||||
// destinations involved in the Pour. The presence of a
|
||||
// commitment in the bucket commitment tree is required
|
||||
// to spend it.
|
||||
boost::array<uint256, 2> commitments;
|
||||
|
||||
// Ciphertexts
|
||||
// These are encrypted using ECIES. They are used to
|
||||
// transfer metadata and seeds to generate trapdoors
|
||||
// for the recipient to spend the value.
|
||||
boost::array<std::vector<unsigned char>, 2> ciphertexts;
|
||||
|
||||
// MACs
|
||||
// The verification of the pour requires these MACs
|
||||
// to be provided as an input.
|
||||
boost::array<uint256, 2> macs;
|
||||
|
||||
// Pour proof
|
||||
// This is a zk-SNARK which ensures that this pour is valid.
|
||||
std::string proof;
|
||||
|
||||
CPourTx(): vpub_old(0), vpub_new(0), scriptPubKey(), scriptSig(), anchor(), serials(), commitments(), ciphertexts(), macs(), proof() {
|
||||
|
||||
}
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
READWRITE(vpub_old);
|
||||
READWRITE(vpub_new);
|
||||
READWRITE(scriptPubKey);
|
||||
READWRITE(scriptSig);
|
||||
READWRITE(anchor);
|
||||
READWRITE(serials);
|
||||
READWRITE(commitments);
|
||||
READWRITE(ciphertexts);
|
||||
READWRITE(macs);
|
||||
READWRITE(proof);
|
||||
}
|
||||
|
||||
friend bool operator==(const CPourTx& a, const CPourTx& b)
|
||||
{
|
||||
return (
|
||||
a.vpub_old == b.vpub_old &&
|
||||
a.vpub_new == b.vpub_new &&
|
||||
a.scriptPubKey == b.scriptPubKey &&
|
||||
a.scriptSig == b.scriptSig &&
|
||||
a.anchor == b.anchor &&
|
||||
a.serials == b.serials &&
|
||||
a.commitments == b.commitments &&
|
||||
a.ciphertexts == b.ciphertexts &&
|
||||
a.macs == b.macs &&
|
||||
a.proof == b.proof
|
||||
);
|
||||
}
|
||||
|
||||
friend bool operator!=(const CPourTx& a, const CPourTx& b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
};
|
||||
|
||||
/** An outpoint - a combination of a transaction hash and an index n into its vout */
|
||||
class COutPoint
|
||||
{
|
||||
|
@ -192,6 +286,7 @@ public:
|
|||
const std::vector<CTxIn> vin;
|
||||
const std::vector<CTxOut> vout;
|
||||
const uint32_t nLockTime;
|
||||
const std::vector<CPourTx> vpour;
|
||||
|
||||
/** Construct a CTransaction that qualifies as IsNull() */
|
||||
CTransaction();
|
||||
|
@ -210,6 +305,9 @@ public:
|
|||
READWRITE(*const_cast<std::vector<CTxIn>*>(&vin));
|
||||
READWRITE(*const_cast<std::vector<CTxOut>*>(&vout));
|
||||
READWRITE(*const_cast<uint32_t*>(&nLockTime));
|
||||
if (nVersion >= 2) {
|
||||
READWRITE(*const_cast<std::vector<CPourTx>*>(&vpour));
|
||||
}
|
||||
if (ser_action.ForRead())
|
||||
UpdateHash();
|
||||
}
|
||||
|
@ -258,6 +356,7 @@ struct CMutableTransaction
|
|||
std::vector<CTxIn> vin;
|
||||
std::vector<CTxOut> vout;
|
||||
uint32_t nLockTime;
|
||||
std::vector<CPourTx> vpour;
|
||||
|
||||
CMutableTransaction();
|
||||
CMutableTransaction(const CTransaction& tx);
|
||||
|
@ -271,6 +370,9 @@ struct CMutableTransaction
|
|||
READWRITE(vin);
|
||||
READWRITE(vout);
|
||||
READWRITE(nLockTime);
|
||||
if (nVersion >= 2) {
|
||||
READWRITE(vpour);
|
||||
}
|
||||
}
|
||||
|
||||
/** Compute the hash of this CMutableTransaction. This is computed on the
|
||||
|
|
|
@ -1070,6 +1070,26 @@ public:
|
|||
SerializeOutput(s, nOutput, nType, nVersion);
|
||||
// Serialize nLockTime
|
||||
::Serialize(s, txTo.nLockTime, nType, nVersion);
|
||||
|
||||
// Serialize vpour
|
||||
if (txTo.nVersion >= 2) {
|
||||
// TODO:
|
||||
//
|
||||
// SIGHASH_* functions will hash portions of
|
||||
// the transaction for use in signatures. This
|
||||
// keeps the pour cryptographically bound to
|
||||
// the transaction from the perspective of the
|
||||
// inputs (but not from the perspective of the
|
||||
// pour).
|
||||
//
|
||||
// This must be rectified in the future.
|
||||
// See zcash/#529
|
||||
//
|
||||
// It will be necessary to change this API to
|
||||
// be abstract over whether an input script is
|
||||
// being skipped or a pour is being skipped.
|
||||
::Serialize(s, txTo.vpour, nType, nVersion);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/array.hpp>
|
||||
|
||||
class CScript;
|
||||
|
||||
static const unsigned int MAX_SIZE = 0x02000000;
|
||||
|
@ -506,6 +508,13 @@ extern inline unsigned int GetSerializeSize(const CScript& v, int nType, int nVe
|
|||
template<typename Stream> void Serialize(Stream& os, const CScript& v, int nType, int nVersion);
|
||||
template<typename Stream> void Unserialize(Stream& is, CScript& v, int nType, int nVersion);
|
||||
|
||||
/**
|
||||
* array
|
||||
*/
|
||||
template<typename T, std::size_t N> unsigned int GetSerializeSize(const boost::array<T, N> &item, int nType, int nVersion);
|
||||
template<typename Stream, typename T, std::size_t N> void Serialize(Stream& os, const boost::array<T, N>& item, int nType, int nVersion);
|
||||
template<typename Stream, typename T, std::size_t N> void Unserialize(Stream& is, boost::array<T, N>& item, int nType, int nVersion);
|
||||
|
||||
/**
|
||||
* pair
|
||||
*/
|
||||
|
@ -698,6 +707,35 @@ void Unserialize(Stream& is, CScript& v, int nType, int nVersion)
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* array
|
||||
*/
|
||||
template<typename T, std::size_t N>
|
||||
unsigned int GetSerializeSize(const boost::array<T, N> &item, int nType, int nVersion)
|
||||
{
|
||||
unsigned int size = 0;
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
size += GetSerializeSize(item[0], nType, nVersion);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
template<typename Stream, typename T, std::size_t N>
|
||||
void Serialize(Stream& os, const boost::array<T, N>& item, int nType, int nVersion)
|
||||
{
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
Serialize(os, item[i], nType, nVersion);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Stream, typename T, std::size_t N>
|
||||
void Unserialize(Stream& is, boost::array<T, N>& item, int nType, int nVersion)
|
||||
{
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
Unserialize(is, item[i], nType, nVersion);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* pair
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -6,6 +6,7 @@
|
|||
#include "streams.h"
|
||||
#include "hash.h"
|
||||
#include "test/test_bitcoin.h"
|
||||
#include "utilstrencodings.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
@ -15,6 +16,39 @@ using namespace std;
|
|||
|
||||
BOOST_FIXTURE_TEST_SUITE(serialize_tests, BasicTestingSetup)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(boost_arrays)
|
||||
{
|
||||
boost::array<std::string, 2> test_case = {string("zub"), string("baz")};
|
||||
CDataStream ss(SER_DISK, 0);
|
||||
ss << test_case;
|
||||
|
||||
auto hash = Hash(ss.begin(), ss.end());
|
||||
|
||||
BOOST_CHECK_MESSAGE("037a75620362617a" == HexStr(ss.begin(), ss.end()), HexStr(ss.begin(), ss.end()));
|
||||
BOOST_CHECK_MESSAGE(hash == uint256S("13cb12b2dd098dced0064fe4897c97f907ba3ed36ae470c2e7fc2b1111eba35a"), "actually got: " << hash.ToString());
|
||||
|
||||
{
|
||||
// note: boost array of size 2 should serialize to be the same as a tuple
|
||||
std::pair<std::string, std::string> test_case_2 = {string("zub"), string("baz")};
|
||||
|
||||
CDataStream ss2(SER_DISK, 0);
|
||||
ss2 << test_case_2;
|
||||
|
||||
auto hash2 = Hash(ss2.begin(), ss2.end());
|
||||
|
||||
BOOST_CHECK(hash == hash2);
|
||||
}
|
||||
|
||||
boost::array<std::string, 2> decoded_test_case;
|
||||
ss >> decoded_test_case;
|
||||
|
||||
BOOST_CHECK(decoded_test_case == test_case);
|
||||
|
||||
boost::array<int32_t, 2> test = {100, 200};
|
||||
|
||||
BOOST_CHECK_EQUAL(GetSerializeSize(test, 0, 0), 8);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(sizes)
|
||||
{
|
||||
BOOST_CHECK_EQUAL(sizeof(char), GetSerializeSize(char(0), 0));
|
||||
|
|
|
@ -101,6 +101,7 @@ void static RandomTransaction(CMutableTransaction &tx, bool fSingle) {
|
|||
tx.nLockTime = (insecure_rand() % 2) ? insecure_rand() : 0;
|
||||
int ins = (insecure_rand() % 4) + 1;
|
||||
int outs = fSingle ? ins : (insecure_rand() % 4) + 1;
|
||||
int pours = (insecure_rand() % 4);
|
||||
for (int in = 0; in < ins; in++) {
|
||||
tx.vin.push_back(CTxIn());
|
||||
CTxIn &txin = tx.vin.back();
|
||||
|
@ -115,6 +116,32 @@ void static RandomTransaction(CMutableTransaction &tx, bool fSingle) {
|
|||
txout.nValue = insecure_rand() % 100000000;
|
||||
RandomScript(txout.scriptPubKey);
|
||||
}
|
||||
if (tx.nVersion >= 2) {
|
||||
for (int pour = 0; pour < pours; pour++) {
|
||||
CPourTx pourtx;
|
||||
pourtx.vpub_old = insecure_rand() % 100000000;
|
||||
pourtx.vpub_new = insecure_rand() % 100000000;
|
||||
RandomScript(pourtx.scriptPubKey);
|
||||
RandomScript(pourtx.scriptSig);
|
||||
pourtx.anchor = GetRandHash();
|
||||
pourtx.serials[0] = GetRandHash();
|
||||
pourtx.serials[1] = GetRandHash();
|
||||
pourtx.ciphertexts[0] = {insecure_rand() % 100, insecure_rand() % 100};
|
||||
pourtx.ciphertexts[1] = {insecure_rand() % 100, insecure_rand() % 100};
|
||||
pourtx.macs[0] = GetRandHash();
|
||||
pourtx.macs[1] = GetRandHash();
|
||||
{
|
||||
std::vector<unsigned char> txt;
|
||||
int prooflen = insecure_rand() % 1000;
|
||||
for (int i = 0; i < prooflen; i++) {
|
||||
txt.push_back(insecure_rand() % 256);
|
||||
}
|
||||
pourtx.proof = std::string(txt.begin(), txt.end());
|
||||
}
|
||||
|
||||
tx.vpour.push_back(pourtx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(sighash_tests, BasicTestingSetup)
|
||||
|
|
Loading…
Reference in New Issue