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() : 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
|
uint256 CMutableTransaction::GetHash() const
|
||||||
{
|
{
|
||||||
|
@ -72,9 +72,9 @@ void CTransaction::UpdateHash() const
|
||||||
*const_cast<uint256*>(&hash) = SerializeHash(*this);
|
*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();
|
UpdateHash();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,6 +83,7 @@ CTransaction& CTransaction::operator=(const CTransaction &tx) {
|
||||||
*const_cast<std::vector<CTxIn>*>(&vin) = tx.vin;
|
*const_cast<std::vector<CTxIn>*>(&vin) = tx.vin;
|
||||||
*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<std::vector<CPourTx>*>(&vpour) = tx.vpour;
|
||||||
*const_cast<uint256*>(&hash) = tx.hash;
|
*const_cast<uint256*>(&hash) = tx.hash;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,100 @@
|
||||||
#include "serialize.h"
|
#include "serialize.h"
|
||||||
#include "uint256.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 */
|
/** An outpoint - a combination of a transaction hash and an index n into its vout */
|
||||||
class COutPoint
|
class COutPoint
|
||||||
{
|
{
|
||||||
|
@ -192,6 +286,7 @@ public:
|
||||||
const std::vector<CTxIn> vin;
|
const std::vector<CTxIn> vin;
|
||||||
const std::vector<CTxOut> vout;
|
const std::vector<CTxOut> vout;
|
||||||
const uint32_t nLockTime;
|
const uint32_t nLockTime;
|
||||||
|
const std::vector<CPourTx> vpour;
|
||||||
|
|
||||||
/** Construct a CTransaction that qualifies as IsNull() */
|
/** Construct a CTransaction that qualifies as IsNull() */
|
||||||
CTransaction();
|
CTransaction();
|
||||||
|
@ -210,6 +305,9 @@ public:
|
||||||
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 (nVersion >= 2) {
|
||||||
|
READWRITE(*const_cast<std::vector<CPourTx>*>(&vpour));
|
||||||
|
}
|
||||||
if (ser_action.ForRead())
|
if (ser_action.ForRead())
|
||||||
UpdateHash();
|
UpdateHash();
|
||||||
}
|
}
|
||||||
|
@ -258,6 +356,7 @@ struct CMutableTransaction
|
||||||
std::vector<CTxIn> vin;
|
std::vector<CTxIn> vin;
|
||||||
std::vector<CTxOut> vout;
|
std::vector<CTxOut> vout;
|
||||||
uint32_t nLockTime;
|
uint32_t nLockTime;
|
||||||
|
std::vector<CPourTx> vpour;
|
||||||
|
|
||||||
CMutableTransaction();
|
CMutableTransaction();
|
||||||
CMutableTransaction(const CTransaction& tx);
|
CMutableTransaction(const CTransaction& tx);
|
||||||
|
@ -271,6 +370,9 @@ struct CMutableTransaction
|
||||||
READWRITE(vin);
|
READWRITE(vin);
|
||||||
READWRITE(vout);
|
READWRITE(vout);
|
||||||
READWRITE(nLockTime);
|
READWRITE(nLockTime);
|
||||||
|
if (nVersion >= 2) {
|
||||||
|
READWRITE(vpour);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Compute the hash of this CMutableTransaction. This is computed on the
|
/** Compute the hash of this CMutableTransaction. This is computed on the
|
||||||
|
|
|
@ -1070,6 +1070,26 @@ public:
|
||||||
SerializeOutput(s, nOutput, nType, nVersion);
|
SerializeOutput(s, nOutput, nType, nVersion);
|
||||||
// Serialize nLockTime
|
// Serialize nLockTime
|
||||||
::Serialize(s, txTo.nLockTime, nType, nVersion);
|
::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 <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include <boost/array.hpp>
|
||||||
|
|
||||||
class CScript;
|
class CScript;
|
||||||
|
|
||||||
static const unsigned int MAX_SIZE = 0x02000000;
|
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 Serialize(Stream& os, const CScript& v, int nType, int nVersion);
|
||||||
template<typename Stream> void Unserialize(Stream& is, 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
|
* 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
|
* pair
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -6,6 +6,7 @@
|
||||||
#include "streams.h"
|
#include "streams.h"
|
||||||
#include "hash.h"
|
#include "hash.h"
|
||||||
#include "test/test_bitcoin.h"
|
#include "test/test_bitcoin.h"
|
||||||
|
#include "utilstrencodings.h"
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
@ -15,6 +16,39 @@ using namespace std;
|
||||||
|
|
||||||
BOOST_FIXTURE_TEST_SUITE(serialize_tests, BasicTestingSetup)
|
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_AUTO_TEST_CASE(sizes)
|
||||||
{
|
{
|
||||||
BOOST_CHECK_EQUAL(sizeof(char), GetSerializeSize(char(0), 0));
|
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;
|
tx.nLockTime = (insecure_rand() % 2) ? insecure_rand() : 0;
|
||||||
int ins = (insecure_rand() % 4) + 1;
|
int ins = (insecure_rand() % 4) + 1;
|
||||||
int outs = fSingle ? 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++) {
|
for (int in = 0; in < ins; in++) {
|
||||||
tx.vin.push_back(CTxIn());
|
tx.vin.push_back(CTxIn());
|
||||||
CTxIn &txin = tx.vin.back();
|
CTxIn &txin = tx.vin.back();
|
||||||
|
@ -115,6 +116,32 @@ void static RandomTransaction(CMutableTransaction &tx, bool fSingle) {
|
||||||
txout.nValue = insecure_rand() % 100000000;
|
txout.nValue = insecure_rand() % 100000000;
|
||||||
RandomScript(txout.scriptPubKey);
|
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)
|
BOOST_FIXTURE_TEST_SUITE(sighash_tests, BasicTestingSetup)
|
||||||
|
|
Loading…
Reference in New Issue