Added CPourTx verification and construction routines.
This commit is contained in:
parent
69761d8269
commit
96c31d163f
|
@ -2,8 +2,8 @@ package=libzerocash
|
||||||
$(package)_download_path=https://github.com/Electric-Coin-Company/$(package)/archive/
|
$(package)_download_path=https://github.com/Electric-Coin-Company/$(package)/archive/
|
||||||
$(package)_file_name=$(package)-$($(package)_git_commit).tar.gz
|
$(package)_file_name=$(package)-$($(package)_git_commit).tar.gz
|
||||||
$(package)_download_file=$($(package)_git_commit).tar.gz
|
$(package)_download_file=$($(package)_git_commit).tar.gz
|
||||||
$(package)_sha256_hash=c758b1f2b3372fb0e228442745668d0498a183cd0a4bcc423271e4ff3ddde85e
|
$(package)_sha256_hash=ef9cd53db6eedea3a5d24551d16d9f23dd52277e91296a14539faa027770ad23
|
||||||
$(package)_git_commit=69df6c95d97a1f1ee1fece0a6a7eef7d6a577dbc
|
$(package)_git_commit=e79cd2dfee8213d49b6c2a8b2353a38d7563c965
|
||||||
|
|
||||||
$(package)_dependencies=libsnark crypto++ openssl boost libgmp
|
$(package)_dependencies=libsnark crypto++ openssl boost libgmp
|
||||||
$(package)_patches=
|
$(package)_patches=
|
||||||
|
|
|
@ -9,6 +9,87 @@
|
||||||
#include "tinyformat.h"
|
#include "tinyformat.h"
|
||||||
#include "utilstrencodings.h"
|
#include "utilstrencodings.h"
|
||||||
|
|
||||||
|
#include "libzerocash/PourProver.h"
|
||||||
|
#include "libzerocash/PourTransaction.h"
|
||||||
|
|
||||||
|
template<std::size_t N>
|
||||||
|
boost::array<std::vector<unsigned char>, N> uint256_to_array(const boost::array<uint256, N>& in) {
|
||||||
|
boost::array<std::vector<unsigned char>, N> result;
|
||||||
|
for (size_t i = 0; i < N; i++) {
|
||||||
|
result[i] = std::vector<unsigned char>(in[i].begin(), in[i].end());
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<std::size_t N>
|
||||||
|
boost::array<uint256, N> unsigned_char_vector_array_to_uint256_array(const boost::array<std::vector<unsigned char>, N>& in) {
|
||||||
|
boost::array<uint256, N> result;
|
||||||
|
for (size_t i = 0; i < N; i++) {
|
||||||
|
result[i] = uint256(in[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
CPourTx::CPourTx(ZerocashParams& params,
|
||||||
|
const CScript& scriptPubKey,
|
||||||
|
const uint256& anchor,
|
||||||
|
const boost::array<PourInput, NUM_POUR_INPUTS>& inputs,
|
||||||
|
const boost::array<PourOutput, NUM_POUR_OUTPUTS>& outputs,
|
||||||
|
CAmount vpub_old,
|
||||||
|
CAmount vpub_new) : scriptSig(), scriptPubKey(scriptPubKey), vpub_old(vpub_old), vpub_new(vpub_new), anchor(anchor)
|
||||||
|
{
|
||||||
|
uint256 scriptPubKeyHash;
|
||||||
|
{
|
||||||
|
CHashWriter ss(SER_GETHASH, 0);
|
||||||
|
ss << scriptPubKey;
|
||||||
|
scriptPubKeyHash = ss.GetHash();
|
||||||
|
}
|
||||||
|
|
||||||
|
PourTransaction pourtx(params,
|
||||||
|
std::vector<unsigned char>(scriptPubKeyHash.begin(), scriptPubKeyHash.end()),
|
||||||
|
std::vector<unsigned char>(anchor.begin(), anchor.end()),
|
||||||
|
std::vector<PourInput>(inputs.begin(), inputs.end()),
|
||||||
|
std::vector<PourOutput>(outputs.begin(), outputs.end()),
|
||||||
|
vpub_old,
|
||||||
|
vpub_new);
|
||||||
|
|
||||||
|
boost::array<std::vector<unsigned char>, NUM_POUR_INPUTS> serials_bv;
|
||||||
|
boost::array<std::vector<unsigned char>, NUM_POUR_OUTPUTS> commitments_bv;
|
||||||
|
boost::array<std::vector<unsigned char>, NUM_POUR_INPUTS> macs_bv;
|
||||||
|
boost::array<std::string, NUM_POUR_OUTPUTS> ciphertexts_bv;
|
||||||
|
|
||||||
|
proof = pourtx.unpack(serials_bv, commitments_bv, macs_bv, ciphertexts_bv);
|
||||||
|
serials = unsigned_char_vector_array_to_uint256_array(serials_bv);
|
||||||
|
commitments = unsigned_char_vector_array_to_uint256_array(commitments_bv);
|
||||||
|
macs = unsigned_char_vector_array_to_uint256_array(macs_bv);
|
||||||
|
|
||||||
|
ciphertexts = ciphertexts_bv;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CPourTx::Verify(ZerocashParams& params) const {
|
||||||
|
// Compute the hash of the scriptPubKey.
|
||||||
|
uint256 scriptPubKeyHash;
|
||||||
|
{
|
||||||
|
CHashWriter ss(SER_GETHASH, 0);
|
||||||
|
ss << scriptPubKey;
|
||||||
|
scriptPubKeyHash = ss.GetHash();
|
||||||
|
}
|
||||||
|
|
||||||
|
return PourProver::VerifyProof(
|
||||||
|
params,
|
||||||
|
std::vector<unsigned char>(scriptPubKeyHash.begin(), scriptPubKeyHash.end()),
|
||||||
|
std::vector<unsigned char>(anchor.begin(), anchor.end()),
|
||||||
|
vpub_old,
|
||||||
|
vpub_new,
|
||||||
|
uint256_to_array<NUM_POUR_INPUTS>(serials),
|
||||||
|
uint256_to_array<NUM_POUR_OUTPUTS>(commitments),
|
||||||
|
uint256_to_array<NUM_POUR_INPUTS>(macs),
|
||||||
|
proof
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
std::string COutPoint::ToString() const
|
std::string COutPoint::ToString() const
|
||||||
{
|
{
|
||||||
return strprintf("COutPoint(%s, %u)", hash.ToString().substr(0,10), n);
|
return strprintf("COutPoint(%s, %u)", hash.ToString().substr(0,10), n);
|
||||||
|
|
|
@ -13,6 +13,15 @@
|
||||||
|
|
||||||
#include <boost/array.hpp>
|
#include <boost/array.hpp>
|
||||||
|
|
||||||
|
#include "libzerocash/ZerocashParams.h"
|
||||||
|
#include "libzerocash/PourInput.h"
|
||||||
|
#include "libzerocash/PourOutput.h"
|
||||||
|
|
||||||
|
using namespace libzerocash;
|
||||||
|
|
||||||
|
static const unsigned int NUM_POUR_INPUTS = 2;
|
||||||
|
static const unsigned int NUM_POUR_OUTPUTS = 2;
|
||||||
|
|
||||||
class CPourTx
|
class CPourTx
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -39,25 +48,25 @@ public:
|
||||||
// are derived from the secrets placed in the bucket
|
// are derived from the secrets placed in the bucket
|
||||||
// and the secret spend-authority key known by the
|
// and the secret spend-authority key known by the
|
||||||
// spender.
|
// spender.
|
||||||
boost::array<uint256, 2> serials;
|
boost::array<uint256, NUM_POUR_INPUTS> serials;
|
||||||
|
|
||||||
// Bucket commitments are introduced into the commitment
|
// Bucket commitments are introduced into the commitment
|
||||||
// tree, blinding the public about the values and
|
// tree, blinding the public about the values and
|
||||||
// destinations involved in the Pour. The presence of a
|
// destinations involved in the Pour. The presence of a
|
||||||
// commitment in the bucket commitment tree is required
|
// commitment in the bucket commitment tree is required
|
||||||
// to spend it.
|
// to spend it.
|
||||||
boost::array<uint256, 2> commitments;
|
boost::array<uint256, NUM_POUR_OUTPUTS> commitments;
|
||||||
|
|
||||||
// Ciphertexts
|
// Ciphertexts
|
||||||
// These are encrypted using ECIES. They are used to
|
// These are encrypted using ECIES. They are used to
|
||||||
// transfer metadata and seeds to generate trapdoors
|
// transfer metadata and seeds to generate trapdoors
|
||||||
// for the recipient to spend the value.
|
// for the recipient to spend the value.
|
||||||
boost::array<std::vector<unsigned char>, 2> ciphertexts;
|
boost::array<std::string, NUM_POUR_OUTPUTS> ciphertexts;
|
||||||
|
|
||||||
// MACs
|
// MACs
|
||||||
// The verification of the pour requires these MACs
|
// The verification of the pour requires these MACs
|
||||||
// to be provided as an input.
|
// to be provided as an input.
|
||||||
boost::array<uint256, 2> macs;
|
boost::array<uint256, NUM_POUR_INPUTS> macs;
|
||||||
|
|
||||||
// Pour proof
|
// Pour proof
|
||||||
// This is a zk-SNARK which ensures that this pour is valid.
|
// This is a zk-SNARK which ensures that this pour is valid.
|
||||||
|
@ -67,6 +76,18 @@ public:
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CPourTx(ZerocashParams& params,
|
||||||
|
const CScript& scriptPubKey,
|
||||||
|
const uint256& rt,
|
||||||
|
const boost::array<PourInput, NUM_POUR_INPUTS>& inputs,
|
||||||
|
const boost::array<PourOutput, NUM_POUR_OUTPUTS>& outputs,
|
||||||
|
CAmount vpub_old,
|
||||||
|
CAmount vpub_new
|
||||||
|
);
|
||||||
|
|
||||||
|
// Verifies that the pour proof is correct.
|
||||||
|
bool Verify(ZerocashParams& params) const;
|
||||||
|
|
||||||
ADD_SERIALIZE_METHODS;
|
ADD_SERIALIZE_METHODS;
|
||||||
|
|
||||||
template <typename Stream, typename Operation>
|
template <typename Stream, typename Operation>
|
||||||
|
|
|
@ -24,8 +24,16 @@
|
||||||
#include <boost/assign/list_of.hpp>
|
#include <boost/assign/list_of.hpp>
|
||||||
#include "json/json_spirit_writer_template.h"
|
#include "json/json_spirit_writer_template.h"
|
||||||
|
|
||||||
|
#include "libzerocash/ZerocashParams.h"
|
||||||
|
#include "libzerocash/IncrementalMerkleTree.h"
|
||||||
|
#include "libzerocash/PourInput.h"
|
||||||
|
#include "libzerocash/PourOutput.h"
|
||||||
|
#include "libzerocash/Address.h"
|
||||||
|
#include "libzerocash/Coin.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace json_spirit;
|
using namespace json_spirit;
|
||||||
|
using namespace libzerocash;
|
||||||
|
|
||||||
// In script_tests.cpp
|
// In script_tests.cpp
|
||||||
extern Array read_json(const std::string& jsondata);
|
extern Array read_json(const std::string& jsondata);
|
||||||
|
@ -285,6 +293,95 @@ SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet)
|
||||||
return dummyTransactions;
|
return dummyTransactions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(test_basic_pour_verification)
|
||||||
|
{
|
||||||
|
// We only check that pours are constructed properly
|
||||||
|
// and verify properly here. libsnark tends to segfault
|
||||||
|
// when our snarks or what-have-you are invalid, so
|
||||||
|
// we can't really catch everything here.
|
||||||
|
//
|
||||||
|
// See #471, #520, #459 and probably others.
|
||||||
|
//
|
||||||
|
// There may be ways to use boost tests to catch failing
|
||||||
|
// threads or processes (?) but they appear to not work
|
||||||
|
// on all platforms and would gently push us down an ugly
|
||||||
|
// path. We should just fix the assertions.
|
||||||
|
//
|
||||||
|
// Also, it's generally libzerocash's job to ensure
|
||||||
|
// the integrity of the scheme through its own tests.
|
||||||
|
|
||||||
|
static const unsigned int TEST_TREE_DEPTH = 3;
|
||||||
|
|
||||||
|
// construct the r1cs keypair
|
||||||
|
auto keypair = ZerocashParams::GenerateNewKeyPair(TEST_TREE_DEPTH);
|
||||||
|
ZerocashParams p(
|
||||||
|
TEST_TREE_DEPTH,
|
||||||
|
&keypair
|
||||||
|
);
|
||||||
|
|
||||||
|
// construct a merkle tree
|
||||||
|
IncrementalMerkleTree merkleTree(TEST_TREE_DEPTH);
|
||||||
|
Address addr = Address::CreateNewRandomAddress();
|
||||||
|
Coin coin(addr.getPublicAddress(), 100);
|
||||||
|
|
||||||
|
// commitment from coin
|
||||||
|
std::vector<bool> commitment(ZC_CM_SIZE * 8);
|
||||||
|
convertBytesVectorToVector(coin.getCoinCommitment().getCommitmentValue(), commitment);
|
||||||
|
|
||||||
|
// insert commitment into the merkle tree
|
||||||
|
std::vector<bool> index;
|
||||||
|
merkleTree.insertElement(commitment, index);
|
||||||
|
|
||||||
|
// compute the merkle root we will be working with
|
||||||
|
vector<unsigned char> rt(ZC_ROOT_SIZE);
|
||||||
|
{
|
||||||
|
vector<bool> root_bv(ZC_ROOT_SIZE * 8);
|
||||||
|
merkleTree.getRootValue(root_bv);
|
||||||
|
convertVectorToBytesVector(root_bv, rt);
|
||||||
|
}
|
||||||
|
|
||||||
|
merkle_authentication_path path(TEST_TREE_DEPTH);
|
||||||
|
merkleTree.getWitness(index, path);
|
||||||
|
|
||||||
|
// create CPourTx
|
||||||
|
CScript scriptPubKey;
|
||||||
|
boost::array<PourInput, 2> inputs = {
|
||||||
|
PourInput(coin, addr, convertVectorToInt(index), path),
|
||||||
|
PourInput(TEST_TREE_DEPTH) // dummy input of zero value
|
||||||
|
};
|
||||||
|
boost::array<PourOutput, 2> outputs = {
|
||||||
|
PourOutput(50),
|
||||||
|
PourOutput(50)
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
CPourTx pourtx(p, scriptPubKey, uint256(rt), inputs, outputs, 0, 0);
|
||||||
|
BOOST_CHECK(pourtx.Verify(p));
|
||||||
|
|
||||||
|
CDataStream ss(SER_DISK, CLIENT_VERSION);
|
||||||
|
ss << pourtx;
|
||||||
|
|
||||||
|
CPourTx pourtx_deserialized;
|
||||||
|
ss >> pourtx_deserialized;
|
||||||
|
|
||||||
|
BOOST_CHECK(pourtx_deserialized == pourtx);
|
||||||
|
BOOST_CHECK(pourtx_deserialized.Verify(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Ensure that the balance equation is working.
|
||||||
|
BOOST_CHECK_THROW(CPourTx(p, scriptPubKey, uint256(rt), inputs, outputs, 10, 0), std::invalid_argument);
|
||||||
|
BOOST_CHECK_THROW(CPourTx(p, scriptPubKey, uint256(rt), inputs, outputs, 0, 10), std::invalid_argument);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Ensure that it won't verify if the root is changed.
|
||||||
|
auto test = CPourTx(p, scriptPubKey, uint256(rt), inputs, outputs, 0, 0);
|
||||||
|
test.anchor = GetRandHash();
|
||||||
|
BOOST_CHECK(!test.Verify(p));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(test_simple_pour_invalidity)
|
BOOST_AUTO_TEST_CASE(test_simple_pour_invalidity)
|
||||||
{
|
{
|
||||||
CMutableTransaction tx;
|
CMutableTransaction tx;
|
||||||
|
|
Loading…
Reference in New Issue