Browse Source

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.
pull/4/head
Sean Bowe 9 years ago
parent
commit
5884044ba9
  1. 7
      src/primitives/transaction.cpp
  2. 102
      src/primitives/transaction.h
  3. 20
      src/script/interpreter.cpp
  4. 38
      src/serialize.h
  5. 1000
      src/test/data/sighash.json
  6. 34
      src/test/serialize_tests.cpp
  7. 27
      src/test/sighash_tests.cpp

7
src/primitives/transaction.cpp

@ -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;
}

102
src/primitives/transaction.h

@ -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

20
src/script/interpreter.cpp

@ -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);
}
}
};

38
src/serialize.h

@ -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

1000
src/test/data/sighash.json

File diff suppressed because one or more lines are too long

34
src/test/serialize_tests.cpp

@ -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));

27
src/test/sighash_tests.cpp

@ -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…
Cancel
Save