Integrate Groth16 verification and proving.

This commit is contained in:
Sean Bowe 2018-05-08 19:56:34 -06:00
parent dd72b5346e
commit b7a6c32178
14 changed files with 154 additions and 53 deletions

View File

@ -3,8 +3,8 @@ $(package)_version=0.1
$(package)_download_path=https://github.com/zcash/$(package)/archive/ $(package)_download_path=https://github.com/zcash/$(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=65363973dfbdde3bc9cb4427724db399c201f580eb42fb02b0b86e043931c90b $(package)_sha256_hash=22cd7afa38b0f60a0b326d1810f1c48204972946d3bf2d2ed6e3feb79e10d554
$(package)_git_commit=5e220695e5961c8619a1095a3b9022509c6c9b9d $(package)_git_commit=fefa46b4c4f2815819e0c7f2d1771239596ea450
$(package)_dependencies=rust $(rust_crates) $(package)_dependencies=rust $(rust_crates)
$(package)_patches=cargo.config $(package)_patches=cargo.config

View File

@ -3,6 +3,7 @@
#include "utilstrencodings.h" #include "utilstrencodings.h"
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include <boost/variant/get.hpp>
#include "zcash/prf.h" #include "zcash/prf.h"
#include "util.h" #include "util.h"
@ -42,7 +43,7 @@ void test_full_api(ZCJoinSplit* js)
boost::array<uint256, 2> commitments; boost::array<uint256, 2> commitments;
uint256 rt = tree.root(); uint256 rt = tree.root();
boost::array<ZCNoteEncryption::Ciphertext, 2> ciphertexts; boost::array<ZCNoteEncryption::Ciphertext, 2> ciphertexts;
ZCProof proof; SproutProof proof;
{ {
boost::array<JSInput, 2> inputs = { boost::array<JSInput, 2> inputs = {
@ -59,6 +60,7 @@ void test_full_api(ZCJoinSplit* js)
// Perform the proof // Perform the proof
proof = js->prove( proof = js->prove(
false,
inputs, inputs,
outputs, outputs,
output_notes, output_notes,
@ -75,9 +77,11 @@ void test_full_api(ZCJoinSplit* js)
); );
} }
auto sprout_proof = boost::relaxed_get<ZCProof, ZCProof, GrothProof>(&proof);
// Verify the transaction: // Verify the transaction:
ASSERT_TRUE(js->verify( ASSERT_TRUE(js->verify(
proof, *sprout_proof,
verifier, verifier,
pubKeyHash, pubKeyHash,
randomSeed, randomSeed,
@ -134,6 +138,7 @@ void test_full_api(ZCJoinSplit* js)
// Perform the proof // Perform the proof
proof = js->prove( proof = js->prove(
false,
inputs, inputs,
outputs, outputs,
output_notes, output_notes,
@ -150,9 +155,11 @@ void test_full_api(ZCJoinSplit* js)
); );
} }
sprout_proof = boost::relaxed_get<ZCProof, ZCProof, GrothProof>(&proof);
// Verify the transaction: // Verify the transaction:
ASSERT_TRUE(js->verify( ASSERT_TRUE(js->verify(
proof, *sprout_proof,
verifier, verifier,
pubKeyHash, pubKeyHash,
randomSeed, randomSeed,
@ -185,7 +192,8 @@ void invokeAPI(
boost::array<SproutNote, 2> output_notes; boost::array<SproutNote, 2> output_notes;
ZCProof proof = js->prove( SproutProof proof = js->prove(
false,
inputs, inputs,
outputs, outputs,
output_notes, output_notes,

View File

@ -43,6 +43,7 @@ TEST(Transaction, JSDescriptionRandomized) {
{ {
auto jsdesc = JSDescription::Randomized( auto jsdesc = JSDescription::Randomized(
false,
*params, pubKeyHash, rt, *params, pubKeyHash, rt,
inputs, outputs, inputs, outputs,
inputMap, outputMap, inputMap, outputMap,
@ -59,6 +60,7 @@ TEST(Transaction, JSDescriptionRandomized) {
{ {
auto jsdesc = JSDescription::Randomized( auto jsdesc = JSDescription::Randomized(
false,
*params, pubKeyHash, rt, *params, pubKeyHash, rt,
inputs, outputs, inputs, outputs,
inputMap, outputMap, inputMap, outputMap,
@ -72,6 +74,7 @@ TEST(Transaction, JSDescriptionRandomized) {
{ {
auto jsdesc = JSDescription::Randomized( auto jsdesc = JSDescription::Randomized(
false,
*params, pubKeyHash, rt, *params, pubKeyHash, rt,
inputs, outputs, inputs, outputs,
inputMap, outputMap, inputMap, outputMap,

View File

@ -9,20 +9,25 @@
#include "tinyformat.h" #include "tinyformat.h"
#include "utilstrencodings.h" #include "utilstrencodings.h"
JSDescription::JSDescription(ZCJoinSplit& params, #include "librustzcash.h"
const uint256& pubKeyHash,
const uint256& anchor, JSDescription::JSDescription(
const boost::array<libzcash::JSInput, ZC_NUM_JS_INPUTS>& inputs, bool makeGrothProof,
const boost::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS>& outputs, ZCJoinSplit& params,
CAmount vpub_old, const uint256& pubKeyHash,
CAmount vpub_new, const uint256& anchor,
bool computeProof, const boost::array<libzcash::JSInput, ZC_NUM_JS_INPUTS>& inputs,
uint256 *esk // payment disclosure const boost::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS>& outputs,
) : vpub_old(vpub_old), vpub_new(vpub_new), anchor(anchor) CAmount vpub_old,
CAmount vpub_new,
bool computeProof,
uint256 *esk // payment disclosure
) : vpub_old(vpub_old), vpub_new(vpub_new), anchor(anchor)
{ {
boost::array<libzcash::SproutNote, ZC_NUM_JS_OUTPUTS> notes; boost::array<libzcash::SproutNote, ZC_NUM_JS_OUTPUTS> notes;
proof = params.prove( proof = params.prove(
makeGrothProof,
inputs, inputs,
outputs, outputs,
notes, notes,
@ -42,19 +47,20 @@ JSDescription::JSDescription(ZCJoinSplit& params,
} }
JSDescription JSDescription::Randomized( JSDescription JSDescription::Randomized(
ZCJoinSplit& params, bool makeGrothProof,
const uint256& pubKeyHash, ZCJoinSplit& params,
const uint256& anchor, const uint256& pubKeyHash,
boost::array<libzcash::JSInput, ZC_NUM_JS_INPUTS>& inputs, const uint256& anchor,
boost::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS>& outputs, boost::array<libzcash::JSInput, ZC_NUM_JS_INPUTS>& inputs,
boost::array<size_t, ZC_NUM_JS_INPUTS>& inputMap, boost::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS>& outputs,
boost::array<size_t, ZC_NUM_JS_OUTPUTS>& outputMap, boost::array<size_t, ZC_NUM_JS_INPUTS>& inputMap,
CAmount vpub_old, boost::array<size_t, ZC_NUM_JS_OUTPUTS>& outputMap,
CAmount vpub_new, CAmount vpub_old,
bool computeProof, CAmount vpub_new,
uint256 *esk, // payment disclosure bool computeProof,
std::function<int(int)> gen uint256 *esk, // payment disclosure
) std::function<int(int)> gen
)
{ {
// Randomize the order of the inputs and outputs // Randomize the order of the inputs and outputs
inputMap = {0, 1}; inputMap = {0, 1};
@ -66,6 +72,7 @@ JSDescription JSDescription::Randomized(
MappedShuffle(outputs.begin(), outputMap.begin(), ZC_NUM_JS_OUTPUTS, gen); MappedShuffle(outputs.begin(), outputMap.begin(), ZC_NUM_JS_OUTPUTS, gen);
return JSDescription( return JSDescription(
makeGrothProof,
params, pubKeyHash, anchor, inputs, outputs, params, pubKeyHash, anchor, inputs, outputs,
vpub_old, vpub_new, computeProof, vpub_old, vpub_new, computeProof,
esk // payment disclosure esk // payment disclosure
@ -105,7 +112,21 @@ public:
bool operator()(const libzcash::GrothProof& proof) const bool operator()(const libzcash::GrothProof& proof) const
{ {
return false; uint256 h_sig = params.h_sig(jsdesc.randomSeed, jsdesc.nullifiers, pubKeyHash);
return librustzcash_sprout_verify(
proof.begin(),
jsdesc.anchor.begin(),
h_sig.begin(),
jsdesc.macs[0].begin(),
jsdesc.macs[1].begin(),
jsdesc.nullifiers[0].begin(),
jsdesc.nullifiers[1].begin(),
jsdesc.commitments[0].begin(),
jsdesc.commitments[1].begin(),
jsdesc.vpub_old,
jsdesc.vpub_new
);
} }
}; };

View File

@ -36,15 +36,6 @@ static_assert(SAPLING_TX_VERSION >= SAPLING_MIN_TX_VERSION,
static_assert(SAPLING_TX_VERSION <= SAPLING_MAX_TX_VERSION, static_assert(SAPLING_TX_VERSION <= SAPLING_MAX_TX_VERSION,
"Sapling tx version must not be higher than maximum"); "Sapling tx version must not be higher than maximum");
static constexpr size_t GROTH_PROOF_SIZE = (
48 + // π_A
96 + // π_B
48); // π_C
namespace libzcash {
typedef boost::array<unsigned char, GROTH_PROOF_SIZE> GrothProof;
}
/** /**
* A shielded input to a transaction. It contains data that describes a Spend transfer. * A shielded input to a transaction. It contains data that describes a Spend transfer.
*/ */
@ -246,11 +237,13 @@ 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.
boost::variant<libzcash::ZCProof, libzcash::GrothProof> proof; libzcash::SproutProof proof;
JSDescription(): vpub_old(0), vpub_new(0) { } JSDescription(): vpub_old(0), vpub_new(0) { }
JSDescription(ZCJoinSplit& params, JSDescription(
bool makeGrothProof,
ZCJoinSplit& params,
const uint256& pubKeyHash, const uint256& pubKeyHash,
const uint256& rt, const uint256& rt,
const boost::array<libzcash::JSInput, ZC_NUM_JS_INPUTS>& inputs, const boost::array<libzcash::JSInput, ZC_NUM_JS_INPUTS>& inputs,
@ -262,6 +255,7 @@ public:
); );
static JSDescription Randomized( static JSDescription Randomized(
bool makeGrothProof,
ZCJoinSplit& params, ZCJoinSplit& params,
const uint256& pubKeyHash, const uint256& pubKeyHash,
const uint256& rt, const uint256& rt,

View File

@ -372,7 +372,7 @@ BOOST_AUTO_TEST_CASE(test_basic_joinsplit_verification)
auto verifier = libzcash::ProofVerifier::Strict(); auto verifier = libzcash::ProofVerifier::Strict();
{ {
JSDescription jsdesc(*pzcashParams, pubKeyHash, rt, inputs, outputs, 0, 0); JSDescription jsdesc(false, *pzcashParams, pubKeyHash, rt, inputs, outputs, 0, 0);
BOOST_CHECK(jsdesc.Verify(*pzcashParams, verifier, pubKeyHash)); BOOST_CHECK(jsdesc.Verify(*pzcashParams, verifier, pubKeyHash));
CDataStream ss(SER_DISK, CLIENT_VERSION); CDataStream ss(SER_DISK, CLIENT_VERSION);
@ -387,13 +387,13 @@ BOOST_AUTO_TEST_CASE(test_basic_joinsplit_verification)
{ {
// Ensure that the balance equation is working. // Ensure that the balance equation is working.
BOOST_CHECK_THROW(JSDescription(*pzcashParams, pubKeyHash, rt, inputs, outputs, 10, 0), std::invalid_argument); BOOST_CHECK_THROW(JSDescription(false, *pzcashParams, pubKeyHash, rt, inputs, outputs, 10, 0), std::invalid_argument);
BOOST_CHECK_THROW(JSDescription(*pzcashParams, pubKeyHash, rt, inputs, outputs, 0, 10), std::invalid_argument); BOOST_CHECK_THROW(JSDescription(false, *pzcashParams, pubKeyHash, rt, inputs, outputs, 0, 10), std::invalid_argument);
} }
{ {
// Ensure that it won't verify if the root is changed. // Ensure that it won't verify if the root is changed.
auto test = JSDescription(*pzcashParams, pubKeyHash, rt, inputs, outputs, 0, 0); auto test = JSDescription(false, *pzcashParams, pubKeyHash, rt, inputs, outputs, 0, 0);
test.anchor = GetRandHash(); test.anchor = GetRandHash();
BOOST_CHECK(!test.Verify(*pzcashParams, verifier, pubKeyHash)); BOOST_CHECK(!test.Verify(*pzcashParams, verifier, pubKeyHash));
} }

View File

@ -40,7 +40,7 @@ CWalletTx GetValidReceive(ZCJoinSplit& params,
// Prepare JoinSplits // Prepare JoinSplits
uint256 rt; uint256 rt;
JSDescription jsdesc {params, mtx.joinSplitPubKey, rt, JSDescription jsdesc {false, params, mtx.joinSplitPubKey, rt,
inputs, outputs, 2*value, 0, false}; inputs, outputs, 2*value, 0, false};
mtx.vjoinsplit.push_back(jsdesc); mtx.vjoinsplit.push_back(jsdesc);
@ -123,7 +123,7 @@ CWalletTx GetValidSpend(ZCJoinSplit& params,
// Prepare JoinSplits // Prepare JoinSplits
uint256 rt = tree.root(); uint256 rt = tree.root();
JSDescription jsdesc {params, mtx.joinSplitPubKey, rt, JSDescription jsdesc {false, params, mtx.joinSplitPubKey, rt,
inputs, outputs, 0, value, false}; inputs, outputs, 0, value, false};
mtx.vjoinsplit.push_back(jsdesc); mtx.vjoinsplit.push_back(jsdesc);

View File

@ -768,6 +768,7 @@ UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit(
uint256 esk; // payment disclosure - secret uint256 esk; // payment disclosure - secret
JSDescription jsdesc = JSDescription::Randomized( JSDescription jsdesc = JSDescription::Randomized(
mtx.fOverwintered && (mtx.nVersion >= SAPLING_TX_VERSION),
*pzcashParams, *pzcashParams,
joinSplitPubKey_, joinSplitPubKey_,
anchor, anchor,

View File

@ -990,6 +990,7 @@ UniValue AsyncRPCOperation_sendmany::perform_joinsplit(
uint256 esk; // payment disclosure - secret uint256 esk; // payment disclosure - secret
JSDescription jsdesc = JSDescription::Randomized( JSDescription jsdesc = JSDescription::Randomized(
mtx.fOverwintered && (mtx.nVersion >= SAPLING_TX_VERSION),
*pzcashParams, *pzcashParams,
joinSplitPubKey_, joinSplitPubKey_,
anchor, anchor,

View File

@ -359,6 +359,7 @@ UniValue AsyncRPCOperation_shieldcoinbase::perform_joinsplit(ShieldCoinbaseJSInf
uint256 esk; // payment disclosure - secret uint256 esk; // payment disclosure - secret
JSDescription jsdesc = JSDescription::Randomized( JSDescription jsdesc = JSDescription::Randomized(
mtx.fOverwintered && (mtx.nVersion >= SAPLING_TX_VERSION),
*pzcashParams, *pzcashParams,
joinSplitPubKey_, joinSplitPubKey_,
anchor, anchor,

View File

@ -2640,7 +2640,8 @@ UniValue zc_sample_joinsplit(const UniValue& params, bool fHelp)
uint256 pubKeyHash; uint256 pubKeyHash;
uint256 anchor = ZCIncrementalMerkleTree().root(); uint256 anchor = ZCIncrementalMerkleTree().root();
JSDescription samplejoinsplit(*pzcashParams, JSDescription samplejoinsplit(false,
*pzcashParams,
pubKeyHash, pubKeyHash,
anchor, anchor,
{JSInput(), JSInput()}, {JSInput(), JSInput()},
@ -2991,7 +2992,8 @@ UniValue zc_raw_joinsplit(const UniValue& params, bool fHelp)
mtx.nVersion = 2; mtx.nVersion = 2;
mtx.joinSplitPubKey = joinSplitPubKey; mtx.joinSplitPubKey = joinSplitPubKey;
JSDescription jsdesc(*pzcashParams, JSDescription jsdesc(false,
*pzcashParams,
joinSplitPubKey, joinSplitPubKey,
anchor, anchor,
{vjsin[0], vjsin[1]}, {vjsin[0], vjsin[1]},
@ -3667,7 +3669,13 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
size_t txsize = 0; size_t txsize = 0;
for (int i = 0; i < zaddrRecipients.size(); i++) { for (int i = 0; i < zaddrRecipients.size(); i++) {
// TODO Check whether the recipient is a Sprout or Sapling address // TODO Check whether the recipient is a Sprout or Sapling address
mtx.vjoinsplit.push_back(JSDescription()); JSDescription jsdesc;
if (mtx.fOverwintered && (mtx.nVersion >= SAPLING_TX_VERSION)) {
jsdesc.proof = GrothProof();
}
mtx.vjoinsplit.push_back(jsdesc);
} }
CTransaction tx(mtx); CTransaction tx(mtx);
txsize += GetSerializeSize(tx, SER_NETWORK, tx.nVersion); txsize += GetSerializeSize(tx, SER_NETWORK, tx.nVersion);

View File

@ -18,6 +18,10 @@
#include "sync.h" #include "sync.h"
#include "amount.h" #include "amount.h"
#include "librustzcash.h"
#include "streams.h"
#include "version.h"
using namespace libsnark; using namespace libsnark;
namespace libzcash { namespace libzcash {
@ -135,7 +139,8 @@ public:
} }
} }
ZCProof prove( SproutProof prove(
bool makeGrothProof,
const boost::array<JSInput, NumInputs>& inputs, const boost::array<JSInput, NumInputs>& inputs,
const boost::array<JSOutput, NumOutputs>& outputs, const boost::array<JSOutput, NumOutputs>& outputs,
boost::array<SproutNote, NumOutputs>& out_notes, boost::array<SproutNote, NumOutputs>& out_notes,
@ -266,6 +271,55 @@ public:
out_macs[i] = PRF_pk(inputs[i].key, i, h_sig); out_macs[i] = PRF_pk(inputs[i].key, i, h_sig);
} }
if (makeGrothProof) {
if (!computeProof) {
return GrothProof();
}
GrothProof proof;
CDataStream ss1(SER_NETWORK, PROTOCOL_VERSION);
ss1 << inputs[0].witness.path();
std::vector<unsigned char> auth1(ss1.begin(), ss1.end());
CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION);
ss2 << inputs[1].witness.path();
std::vector<unsigned char> auth2(ss2.begin(), ss2.end());
librustzcash_sprout_prove(
proof.begin(),
phi.begin(),
rt.begin(),
h_sig.begin(),
inputs[0].key.begin(),
inputs[0].note.value(),
inputs[0].note.rho.begin(),
inputs[0].note.r.begin(),
auth1.data(),
inputs[1].key.begin(),
inputs[1].note.value(),
inputs[1].note.rho.begin(),
inputs[1].note.r.begin(),
auth2.data(),
out_notes[0].a_pk.begin(),
out_notes[0].value(),
out_notes[0].r.begin(),
out_notes[1].a_pk.begin(),
out_notes[1].value(),
out_notes[1].r.begin(),
vpub_old,
vpub_new
);
return proof;
}
if (!computeProof) { if (!computeProof) {
return ZCProof(); return ZCProof();
} }

View File

@ -15,6 +15,14 @@
namespace libzcash { namespace libzcash {
static constexpr size_t GROTH_PROOF_SIZE = (
48 + // π_A
96 + // π_B
48); // π_C
typedef boost::array<unsigned char, GROTH_PROOF_SIZE> GrothProof;
typedef boost::variant<ZCProof, GrothProof> SproutProof;
class JSInput { class JSInput {
public: public:
ZCIncrementalWitness witness; ZCIncrementalWitness witness;
@ -59,7 +67,8 @@ public:
const uint256& pubKeyHash const uint256& pubKeyHash
); );
virtual ZCProof prove( virtual SproutProof prove(
bool makeGrothProof,
const boost::array<JSInput, NumInputs>& inputs, const boost::array<JSInput, NumInputs>& inputs,
const boost::array<JSOutput, NumOutputs>& outputs, const boost::array<JSOutput, NumOutputs>& outputs,
boost::array<SproutNote, NumOutputs>& out_notes, boost::array<SproutNote, NumOutputs>& out_notes,

View File

@ -116,7 +116,8 @@ double benchmark_create_joinsplit()
struct timeval tv_start; struct timeval tv_start;
timer_start(tv_start); timer_start(tv_start);
JSDescription jsdesc(*pzcashParams, JSDescription jsdesc(false, // TODO: ?
*pzcashParams,
pubKeyHash, pubKeyHash,
anchor, anchor,
{JSInput(), JSInput()}, {JSInput(), JSInput()},