Auto merge of #908 - ebfull:trafford, r=ebfull

libzcash and new zkSNARK circuit implementation

This PR completes [`libzcash`](https://github.com/zcash/zcash/tree/zc.v0.11.2.latest/src/zcash), the implementation of the [Zcash protocol specification](9bb4410e45/protocol/protocol.pdf) and replacement of [`libzerocash`](https://github.com/Zerocash/libzerocash), our old Zerocash protocol implementation. The new spec comes with some improvements to security and terminology, with minimal differences from the original academic design.

This implementation includes:

* A rewrite of the zkSNARK circuit for `JoinSplit` operations. This rewrite is cleaner, broken up into separate gadgets, easier to audit and review, and fixes some security bugs. (Closes #822, Closes #809, Closes #500, Closes #854)
* A minimal API for interacting with `JoinSplit`s and surrounding primitives. This PR removes almost twice as much code as it introduces. (Closes #877, Closes #315, Closes #824, Closes #798, Closes #707, Closes #512, Closes #247, Closes #128, Closes #514)
This commit is contained in:
zkbot 2016-05-17 01:04:49 +00:00
commit 9e387120eb
76 changed files with 2853 additions and 5974 deletions

4
.gitignore vendored
View File

@ -10,12 +10,16 @@ src/test/test_bitcoin
src/qt/test/test_bitcoin-qt
# zerocash tests and utilities
src/zcash/GenerateParams
src/zerocash/GenerateParamsForFiles
src/zerocash/tests/merkleTest
src/zerocash/tests/utilTest
src/zerocash/tests/zerocashTest
src/zerocash/tests/test_zerocash_pour_ppzksnark
*zcashTest.pk
*zcashTest.vk
# autoreconf
Makefile.in
aclocal.m4

View File

@ -38,9 +38,6 @@ run_test_phase make check '||' \
false ';' \
'}'
run_test_phase "${REPOROOT}/src/zerocash/tests/zerocashTest"
run_test_phase "${REPOROOT}/src/zerocash/tests/test_zerocash_pour_ppzksnark"
exit $SUITE_EXIT_STATUS

View File

@ -34,7 +34,7 @@ function zcashd_massif_stop {
ms_print massif.out
}
RAWTXWITHPOUR=02000000016939161447690d28bb07215b39ae6814bc7fca7a25f11650c0545eced95e365e00000000484730440220309ff19ffdabbfdcaf172c876a8f89b417d15c76a1f821fc2bfff4027586e29602202d0df763fe8cbc133624774e45b123d2e9dc90a6eb875addd4ccb0cb1c0419fc01ffffffff0000000000018091d2ed000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002924bcb835eb38c98d7fc741b9f5058090492b2cca5484446d654cbcc45f664410afafd7c8dc6a971cabf3dbd7786cbffb8cf5cd56c381fae1be232d767329d110aee171f8043d9c2173d2de8d82c700cbf212d545ecbac92ee92f5c9520bb20e82c931a64c8ec14074af68eca1801283d46197c320f640f94b71afc6ce899dff953425c608c1dd6daa461362360245183c17122d2b153329c685d8a79c512f287cfc02b32e723a98ca7657e21fca74884045331403819897fa3b757f3eded5f547ac1b9be65bb909b00339a6c616f92904475336155705b8b69a08371ab3d5567a65b2ca10fe40901767354f8d78c26349650c534586241a4328a406da80f7527f80c4459ca9e41765dedc933100a84f0d3279e409e0a2fc764dfeb2e1095756c4e02169a4eef5b1a5ff9e154c3408cba9fad363c2f0f63f22233ab266fb7ee4e207d8b492daf85b9f86da636b225a63354815010e7a7cd36395134b87d291f2afcc7f24e85374b15e3a411e686038dfbd652eda123b0e7dd6fb83c84d0fe48d10757d1760cd25d8c1bf08a609ea1892ba0c367640b40fee3dcbbaa0a9acd0802ee706de37771c35245a4c1d658db79974e96073407e093b55e730d31016a5e72aa68e3cbd46fd7e087a5311143299fc0987a39982154d40b4b901e22d7afd6e63e543033411105cc6196ed7c0b15424795a9d9ee688909269e5cd44e34b057ea44e9ce0af9a2b2bf5a152fdc45d0f88b728b8133e352f41ed29bf9b2c791860b85acb9ebd39c57fbd65852232b945c0eb8029b972c534dbb7776ded36e32a4ca2105a848232b28a94842f17f9f822f4951054a98c2dd17063597a2c98333bd229a8082093ccc1d763841c341fd605eed5567a4d1bea8487b748002ef6dcdf72ee6043ee294b9182425bafe256751b1d1e813e7f07b8deead9a0be1101e20157fa78cddd2a3902b0fbbb9560352932afd86053020313331383238313537353434373932353434313139313534313739353638353931323731373537393730343539333530323239373031303839383234353733323939303339303630303230353320313637393735353535373337303738353038323038343136333038323338353037323837373932323634313939353232363538313335343735323436393837383934363838363830333732313320302032303634363938313832343636383136343736333439383637323936383337333938313535383336393734363136393233313036313632343339333130393933353132333234393239313735312032303936333639393934373532383338383930393237333532323239393437343930363239373938333536363036393837333833373230313830343839373433333535303936373432343534320a3020313437353035363735303337373337313535393031313233383834323539363032353330383531363837333437393038333834333032393538333335373539363730383339363632353538313620313532323232303839343439323735393031353132303333393931363233303430303038323536373037343830393436333638393532373837373038333335363238343230363139383133353320333138373235353831303334353533313235303839303931383535363430353236333434333534323637393831363039353032333035353733393336313734373733333737333331363532302031373030383032353631393739383733303934313830323135343731303135373433373536313833353632323035393234313836393833363336313733363536343139343139313438313435203020323134373836363032383538303031353339373532363933373535363237383536393333353039313937373034333134383831303537383638383430333832333037323538343438353337322031313331373839313630393538303635323030383130383032323539353136333532313733363932343839363131363232393238363736323933393532303032363237383530363636383731390a302031373639363736313335353231393739343635353034363532343237313237353036333734313531353734363437383730343238303137363334363939393134303338343835383235393937372038323338353937303434343834383739303031383030363131393538323239373732363835333835363832353634363534373834393438333038353533373137353235343235313235363437203020313932393935303230343630393332303635303038323732333937363835363735363332393133373836343334343230353834373238333637393635353630333733383334373337303735343220353138343739303934393431393533303736323338343638353431363231363831393331373239333238393433343136303836323232343938343938343934393132383433313938333836330a302031363937353431353932323334393735343434393138373530333135303631353637303431363531343435353936393439343233383935383333373635363030303931343634383730353332372031313332373338303536323637353438383035313232373838313730313235313131353438393630393934323137313231343232363139303132353132363039323235323337353436373737370a3020313634373338353238383338383431373132353639393134333330323436373437343038343233303237313739323232313139303237383637333438303537303031303437323032323734343120343134373933323338343537393731343135323039353631383331373931383534323835363539363130373634373537323938313033343839383938353138323536353137323331383636300a
RAWTXWITHPOUR=020000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024c7ec193fc98ef10d3cf232d795dd44a8f2c8ab94fd25d136ccc86d118d412ff17838c9497c3d388ec07fbdc99a37c2854a3b6b87b1b4d68bfa7c35bab45aeaf1e34486be8d6b2e0d98a063c277001914ef50dddee4e2215ee58eb3416ffdfdead5ddb03804822868b676fb87d56a34c24b6f7facc476fa3acba6bfe81f7d7dda6be52f7ce9c7e863a1d9177907521f4cf3012b5534edeeb391c92207269a08c3bea8ebc33ab251a6a0ae8407fc1a7ab6e8be36e04dfa8f143a58963133c775f04b154ef7b41107d73d6f2a8fc3cab14ffc44376b712ec7714b4e121dd418f51ed8c6a599ac50ff3e696781bec94ac11d8065f915a0abd6656439f4a7fbc55ff0efcae1403a9d9001504cd167ca97c4ecf9d8f8904ab4d310ac84084c62f04a100423dd933be1c7b33d13bbe22f93aaccb0f79854a739fc1ce38bd13e0a6b6ad3eddac4d457eeb6dff47bc4ebc04428437a9e7e2dd71ac1f631b619c30361f7045aefe8cac34d9bd1f47e8f598cea7b4fe212d81cfcdd69f29e543e846cba4c1a6bae736e85b76f33d3431cbca19bd2243ccac6c30434d564f8920fc597bec7d202d95d010a920df18df9c3e8e15950d105809c7e5d57eb780875df5cc22f9aece88561c03a3da4d70cceef7ade85ed071ebc5e0ca3e166d6d82dfcf79bdef0ac0c02b9b716ea10d9d5daa4af992660c7128191d5487edc9a0951ee3c65a14baa1974db470e783d91e5114e4579608a1bdcce715050650231a952890cce01226f5d6f2c8b1d8868ee51deeaecef0da4f291df1e4e6d2a1e7fc9c00bef293f458f9e4391b396c03e453522f3188d8d0cf12f1d7f69780563ff449592c9b0249ffe6f8ae9f2167acf5094a6fa08f07f5bb36fbe0a94139a090305488923ba108213916f8d3b9de21586b952938b5a2563c3055fc3804581abc9dad45fb3721ea000f835dd7560bf686b057736710106113006fd88053020313436373432373635323732303533373131313331313437363032353839383932363036333330313738353230313030303933373539373534333233363030363530363833303136323538363720313732373130363635383738303430393731313235353835363637383734363934373030393830323731303036313532353732303437393539333437313236373330323734353136393337353720302032303831383832343535373436343637313436323832373235363234393531303135393935343736383438363836383638393133323136373337393239343737353032353533373639383932302031343537363435373732313933313534383536333934303732333434343431313731363130393935313436303035343138323939383036333039313735373039313230353237333636373634350a3020313837333133313335393838363137383534313838333832313234303539363438333132373935313637323436313734363133353930323235383536323436353938323037373136363739353120373731303539343839353436353331343133353635323631363637383731333832363139323534333033353333303230343038353532353435373936343330323238383038353936333035372033393238323630343737373833363032333631383533393033303532383933393434343639383238313430343235303637373630303232393931303339383538323436313031323932393832203231353835373031383531313237343139373433383336383538353733393333323238313230343538343437393833343735393537393736363530373433343731343330383036373331343039203020323037303431373036353339393033313034353239313333393435343035353132383737363534363335303837333334343636343630373930383534343139333334353738343337363538363220343938303931373435383431353030373738343035323137393136333836303031373634313737303433313530343538333637323939373935313736313430383037323933333935363334320a3020313734373431323532313139313134303139323333373338363330373430313233333634363136353539373831333735393639383135313532353934383634383235383036393930363437343620313430393930383734353239353830393935373937393830333038363439313232303132343230393138353332353735393632333832303933383438363636373435373237343739303439383820302031313535303936313835373236323531303431343530393633383238313537363737393233303838383231303632343931363433323032353035393039313630363737333538303236383430392031333537363330343835343433323031353937373136363735343430333233313033393830313734363739333035393630363933393635303931393436353038373130363732303132393531340a302032313737383334373037373231393833313033323139383636353536353838393039313934303138303939393537363830333435383838353232383735373336343231343538343436353731362031333531363133333832303232393737353734363930393130393832383932313435303238313134333539343930383433343137373135363635353135313736303536313437373131383638340a3020313939343535383534323238373237313134353535393633303332353932373130313136323337343831333731333931323838383538343530383933343236333238383039363231333337343020393631333030303238303530383434343630373235353637353935333130393431323539313435343637393338393838333533353434313635313239353434383034363238323939363934350a
case "$1" in
time)

View File

@ -72,21 +72,13 @@ endif
# TODO: rename to libzcash
LIBZEROCASH_H = \
zcash/IncrementalMerkleTree.h \
zerocash/Address.h \
zerocash/CoinCommitment.h \
zerocash/Coin.h \
zerocash/IncrementalMerkleTree.h \
zerocash/MintTransaction.h \
zerocash/PourInput.h \
zerocash/PourOutput.h \
zerocash/PourProver.h \
zerocash/PourTransaction.h \
zerocash/Zerocash.h \
zerocash/ZerocashParams.h \
zerocash/zerocash_pour_params.hpp \
zerocash/utils/util.h \
zcash/NoteEncryption.hpp \
zcash/prf.h
zcash/Address.hpp \
zcash/JoinSplit.hpp \
zcash/Note.hpp \
zcash/prf.h \
zcash/util.h
.PHONY: FORCE
# bitcoin core #
@ -415,19 +407,13 @@ bitcoin_tx_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
# zerocash protocol primitives #
libzerocash_a_SOURCES = \
zcash/IncrementalMerkleTree.cpp \
zerocash/Address.cpp \
zerocash/CoinCommitment.cpp \
zerocash/Coin.cpp \
zerocash/IncrementalMerkleTree.cpp \
zerocash/MintTransaction.cpp \
zerocash/PourInput.cpp \
zerocash/PourOutput.cpp \
zerocash/PourProver.cpp \
zerocash/PourTransaction.cpp \
zerocash/ZerocashParams.cpp \
zerocash/utils/util.cpp \
zcash/NoteEncryption.cpp \
zcash/prf.cpp
zcash/Address.cpp \
zcash/JoinSplit.cpp \
zcash/Note.cpp \
zcash/prf.cpp \
zcash/util.cpp
libzerocash_a_CPPFLAGS = -fPIC -DCURVE_ALT_BN128 -DBOOST_SPIRIT_THREADSAFE -DHAVE_BUILD_INFO -D__STDC_FORMAT_MACROS -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -std=c++11 -pipe -O2 -O0 -g -Wstack-protector -fstack-protector-all -fPIE -fvisibility=hidden -DSTATIC $(BITCOIN_INCLUDES)
@ -467,7 +453,7 @@ CLEANFILES = leveldb/libleveldb.a leveldb/libmemenv.a *.gcda *.gcno
DISTCLEANFILES = obj/build.h
EXTRA_DIST = leveldb libzerocash/Makefile
EXTRA_DIST = leveldb
clean-local:
-$(MAKE) -C leveldb clean

View File

@ -6,8 +6,10 @@ zcash_gtest_SOURCES = \
gtest/main.cpp \
gtest/test_tautology.cpp \
gtest/test_checktransaction.cpp \
gtest/test_joinsplit.cpp \
gtest/test_noteencryption.cpp \
gtest/test_merkletree.cpp
gtest/test_merkletree.cpp \
gtest/test_circuit.cpp
zcash_gtest_LDADD = -lgtest $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBBITCOIN_UNIVALUE) $(LIBLEVELDB) $(LIBMEMENV) \
$(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1)

View File

@ -1,14 +1,13 @@
bin_PROGRAMS += \
zerocash/GenerateParamsForFiles \
zerocash/tests/utilTest \
zerocash/tests/zerocashTest \
zerocash/tests/test_zerocash_pour_ppzksnark
zcash/GenerateParams \
zerocash/tests/utilTest
# tool for generating our public parameters
zerocash_GenerateParamsForFiles_SOURCES = zerocash/GenerateParamsForFiles.cpp
zerocash_GenerateParamsForFiles_LDADD = \
zcash_GenerateParams_SOURCES = zcash/GenerateParams.cpp
zcash_GenerateParams_LDADD = \
$(BOOST_LIBS) \
$(LIBZEROCASH) \
$(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_CRYPTO) \
$(LIBZEROCASH_LIBS)
@ -20,26 +19,3 @@ zerocash_tests_utilTest_LDADD = \
$(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_CRYPTO) \
$(LIBZEROCASH_LIBS)
# tests for libzerocash APIs
zerocash_tests_zerocashTest_SOURCES = \
zerocash/tests/zerocashTest.cpp \
zerocash/tests/timer.cpp
zerocash_tests_zerocashTest_LDADD = \
$(BOOST_LIBS) \
$(LIBZEROCASH) \
$(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_CRYPTO) \
$(LIBZEROCASH_LIBS)
# tests for our zkSNARK circuit
zerocash_tests_test_zerocash_pour_ppzksnark_SOURCES = zerocash/tests/test_zerocash_pour_ppzksnark.cpp
zerocash_tests_test_zerocash_pour_ppzksnark_LDADD = \
$(BOOST_LIBS) \
$(LIBZEROCASH) \
$(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_CRYPTO) \
$(LIBZEROCASH_LIBS)

View File

@ -0,0 +1,78 @@
#include <gtest/gtest.h>
#include "uint256.h"
#include "zerocash/utils/util.h"
#include "zcash/util.h"
#include <boost/foreach.hpp>
#include <boost/format.hpp>
#include <boost/optional.hpp>
#include "libsnark/common/default_types/r1cs_ppzksnark_pp.hpp"
#include "libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp"
#include "libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_gadget.hpp"
#include "libsnark/gadgetlib1/gadgets/merkle_tree/merkle_tree_check_read_gadget.hpp"
using namespace libsnark;
using namespace libzerocash;
#include "zcash/circuit/utils.tcc"
template<typename FieldT>
void test_value_equals(uint64_t i) {
protoboard<FieldT> pb;
pb_variable_array<FieldT> num;
num.allocate(pb, 64, "");
num.fill_with_bits(pb, uint64_to_bool_vector(i));
pb.add_r1cs_constraint(r1cs_constraint<FieldT>(
packed_addition(num),
FieldT::one(),
FieldT::one() * i
), "");
ASSERT_TRUE(pb.is_satisfied());
}
TEST(circuit, values)
{
default_r1cs_ppzksnark_pp::init_public_params();
typedef Fr<default_r1cs_ppzksnark_pp> FieldT;
test_value_equals<FieldT>(0);
test_value_equals<FieldT>(1);
test_value_equals<FieldT>(3);
test_value_equals<FieldT>(5391);
test_value_equals<FieldT>(883128374);
test_value_equals<FieldT>(173419028459);
test_value_equals<FieldT>(2205843009213693953);
}
TEST(circuit, endianness)
{
std::vector<unsigned char> before = {
0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55,
56, 57, 58, 59, 60, 61, 62, 63
};
auto result = swap_endianness_u64(before);
std::vector<unsigned char> after = {
56, 57, 58, 59, 60, 61, 62, 63,
48, 49, 50, 51, 52, 53, 54, 55,
40, 41, 42, 43, 44, 45, 46, 47,
32, 33, 34, 35, 36, 37, 38, 39,
24, 25, 26, 27, 28, 29, 30, 31,
16, 17, 18, 19, 20, 21, 22, 23,
8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7
};
EXPECT_EQ(after, result);
std::vector<unsigned char> bad = {0, 1, 2, 3};
ASSERT_THROW(swap_endianness_u64(bad), std::length_error);
}

View File

@ -0,0 +1,294 @@
#include <gtest/gtest.h>
#include "utilstrencodings.h"
#include <boost/foreach.hpp>
#include "zcash/prf.h"
#include "zcash/JoinSplit.hpp"
#include "zcash/Note.hpp"
#include "zcash/NoteEncryption.hpp"
#include "zcash/IncrementalMerkleTree.hpp"
using namespace libzcash;
void test_full_api(ZCJoinSplit* js)
{
// The recipient's information.
SpendingKey recipient_key = SpendingKey::random();
PaymentAddress recipient_addr = recipient_key.address();
// Create the commitment tree
ZCIncrementalMerkleTree tree;
// Set up a JoinSplit description
uint256 ephemeralKey;
uint256 randomSeed;
uint64_t vpub_old = 10;
uint64_t vpub_new = 0;
uint256 pubKeyHash = random_uint256();
boost::array<uint256, 2> macs;
boost::array<uint256, 2> nullifiers;
boost::array<uint256, 2> commitments;
uint256 rt = tree.root();
boost::array<ZCNoteEncryption::Ciphertext, 2> ciphertexts;
std::string proof;
{
boost::array<JSInput, 2> inputs = {
JSInput(), // dummy input
JSInput() // dummy input
};
boost::array<JSOutput, 2> outputs = {
JSOutput(recipient_addr, 10),
JSOutput() // dummy output
};
boost::array<Note, 2> output_notes;
// Perform the proof
proof = js->prove(
inputs,
outputs,
output_notes,
ciphertexts,
ephemeralKey,
pubKeyHash,
randomSeed,
macs,
nullifiers,
commitments,
vpub_old,
vpub_new,
rt
);
}
// Verify the transaction:
ASSERT_TRUE(js->verify(
proof,
pubKeyHash,
randomSeed,
macs,
nullifiers,
commitments,
vpub_old,
vpub_new,
rt
));
// Recipient should decrypt
// Now the recipient should spend the money again
auto h_sig = js->h_sig(randomSeed, nullifiers, pubKeyHash);
ZCNoteDecryption decryptor(recipient_key.viewing_key());
auto note_pt = NotePlaintext::decrypt(
decryptor,
ciphertexts[0],
ephemeralKey,
h_sig,
0
);
auto decrypted_note = note_pt.note(recipient_addr);
ASSERT_TRUE(decrypted_note.value == 10);
// Insert the commitments from the last tx into the tree
tree.append(commitments[0]);
auto witness_recipient = tree.witness();
tree.append(commitments[1]);
witness_recipient.append(commitments[1]);
vpub_old = 0;
vpub_new = 1;
rt = tree.root();
pubKeyHash = random_uint256();
{
boost::array<JSInput, 2> inputs = {
JSInput(), // dummy input
JSInput(witness_recipient, decrypted_note, recipient_key)
};
SpendingKey second_recipient = SpendingKey::random();
PaymentAddress second_addr = second_recipient.address();
boost::array<JSOutput, 2> outputs = {
JSOutput(second_addr, 9),
JSOutput() // dummy output
};
boost::array<Note, 2> output_notes;
// Perform the proof
proof = js->prove(
inputs,
outputs,
output_notes,
ciphertexts,
ephemeralKey,
pubKeyHash,
randomSeed,
macs,
nullifiers,
commitments,
vpub_old,
vpub_new,
rt
);
}
// Verify the transaction:
ASSERT_TRUE(js->verify(
proof,
pubKeyHash,
randomSeed,
macs,
nullifiers,
commitments,
vpub_old,
vpub_new,
rt
));
}
TEST(joinsplit, h_sig)
{
auto js = ZCJoinSplit::Unopened();
/*
// by Taylor Hornby
import pyblake2
import binascii
def hSig(randomSeed, nf1, nf2, pubKeyHash):
return pyblake2.blake2b(
data=(randomSeed + nf1 + nf2 + pubKeyHash),
digest_size=32,
person=b"ZcashComputehSig"
).digest()
INCREASING = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"
TEST_VECTORS = [
[b"a" * 32, b"b" * 32, b"c" * 32, b"d" * 32],
[b"\x00" * 32, b"\x00" * 32, b"\x00" * 32, b"\x00" * 32],
[b"\xFF" * 32, b"\xFF" * 32, b"\xFF" * 32, b"\xFF" * 32],
[INCREASING, INCREASING, INCREASING, INCREASING]
]
for test_input in TEST_VECTORS:
print "---"
print "\"" + binascii.hexlify(test_input[0][::-1]) + "\""
print "\"" + binascii.hexlify(test_input[1][::-1]) + "\""
print "\"" + binascii.hexlify(test_input[2][::-1]) + "\""
print "\"" + binascii.hexlify(test_input[3][::-1]) + "\""
print "\"" + binascii.hexlify(hSig(test_input[0], test_input[1], test_input[2], test_input[3])[::-1]) + "\""
*/
std::vector<std::vector<std::string>> tests = {
{
"6161616161616161616161616161616161616161616161616161616161616161",
"6262626262626262626262626262626262626262626262626262626262626262",
"6363636363636363636363636363636363636363636363636363636363636363",
"6464646464646464646464646464646464646464646464646464646464646464",
"a8cba69f1fa329c055756b4af900f8a00b61e44f4cb8a1824ceb58b90a5b8113"
},
{
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
"697322276b5dd93b12fb1fcbd2144b2960f24c73aac6c6a0811447be1e7f1e19"
},
{
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"4961048919f0ca79d49c9378c36a91a8767060001f4212fe6f7d426f3ccf9f32"
},
{
"1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100",
"1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100",
"1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100",
"1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100",
"b61110ec162693bc3d9ca7fb0eec3afd2e278e2f41394b3ff11d7cb761ad4b27"
}
};
BOOST_FOREACH(std::vector<std::string>& v, tests) {
auto expected = js->h_sig(
uint256S(v[0]),
{uint256S(v[1]), uint256S(v[2])},
uint256S(v[3])
);
EXPECT_EQ(expected, uint256S(v[4]));
}
delete js;
}
TEST(joinsplit, full_api_test)
{
auto js = ZCJoinSplit::Generate();
test_full_api(js);
js->saveProvingKey("./zcashTest.pk");
js->saveVerifyingKey("./zcashTest.vk");
delete js;
js = ZCJoinSplit::Unopened();
js->setProvingKeyPath("./zcashTest.pk");
js->loadProvingKey();
js->loadVerifyingKey("./zcashTest.vk");
test_full_api(js);
delete js;
}
TEST(joinsplit, note_plaintexts)
{
uint256 a_sk = uint256S("f6da8716682d600f74fc16bd0187faad6a26b4aa4c24d5c055b216d94516847e");
uint256 a_pk = PRF_addr_a_pk(a_sk);
uint256 sk_enc = ZCNoteEncryption::generate_privkey(a_sk);
uint256 pk_enc = ZCNoteEncryption::generate_pubkey(sk_enc);
PaymentAddress addr_pk(a_pk, pk_enc);
uint256 h_sig;
ZCNoteEncryption encryptor(h_sig);
uint256 epk = encryptor.get_epk();
Note note(a_pk,
1945813,
random_uint256(),
random_uint256()
);
boost::array<unsigned char, ZC_MEMO_SIZE> memo;
NotePlaintext note_pt(note, memo);
ZCNoteEncryption::Ciphertext ct = note_pt.encrypt(encryptor, pk_enc);
ZCNoteDecryption decryptor(sk_enc);
auto decrypted = NotePlaintext::decrypt(decryptor, ct, epk, h_sig, 0);
auto decrypted_note = decrypted.note(addr_pk);
ASSERT_TRUE(decrypted_note.a_pk == note.a_pk);
ASSERT_TRUE(decrypted_note.rho == note.rho);
ASSERT_TRUE(decrypted_note.r == note.r);
ASSERT_TRUE(decrypted_note.value == note.value);
ASSERT_TRUE(decrypted.memo == note_pt.memo);
}

View File

@ -40,7 +40,6 @@ read_json(const std::string& jsondata)
}
#include "zcash/IncrementalMerkleTree.hpp"
#include "zerocash/IncrementalMerkleTree.h"
#include "zerocash/utils/util.h"
//#define PRINT_JSON 1
@ -106,107 +105,11 @@ void expect_test_vector(T& it, const U& expected)
#endif
}
/*
This is a wrapper around the old incremental merkle tree which
attempts to mimic the new API as much as possible so that its
behavior can be compared with the test vectors we use.
*/
class OldIncrementalMerkleTree {
private:
libzerocash::IncrementalMerkleTree* tree;
boost::optional<std::vector<bool>> index;
bool witnessed;
public:
OldIncrementalMerkleTree() : index(boost::none), witnessed(false) {
this->tree = new IncrementalMerkleTree(INCREMENTAL_MERKLE_TREE_DEPTH_TESTING);
}
~OldIncrementalMerkleTree()
{
delete tree;
}
OldIncrementalMerkleTree (const OldIncrementalMerkleTree& other) : index(boost::none), witnessed(false)
{
this->tree = new IncrementalMerkleTree(INCREMENTAL_MERKLE_TREE_DEPTH_TESTING);
this->tree->setTo(*other.tree);
index = other.index;
witnessed = other.witnessed;
}
OldIncrementalMerkleTree& operator= (const OldIncrementalMerkleTree& other)
{
OldIncrementalMerkleTree tmp(other); // re-use copy-constructor
*this = std::move(tmp); // re-use move-assignment
return *this;
}
OldIncrementalMerkleTree& operator= (OldIncrementalMerkleTree&& other)
{
tree->setTo(*other.tree);
index = other.index;
witnessed = other.witnessed;
return *this;
}
libzcash::MerklePath path() {
assert(witnessed);
if (!index) {
throw std::runtime_error("can't create an authentication path for the beginning of the tree");
}
merkle_authentication_path path(INCREMENTAL_MERKLE_TREE_DEPTH_TESTING);
tree->getWitness(*index, path);
libzcash::MerklePath ret;
ret.authentication_path = path;
ret.index = *index;
return ret;
}
uint256 root() {
std::vector<unsigned char> newrt_v(32);
tree->getRootValue(newrt_v);
return uint256(newrt_v);
}
void append(uint256 obj) {
std::vector<bool> new_index;
std::vector<unsigned char> obj_bv(obj.begin(), obj.end());
std::vector<bool> commitment_bv(256);
libzerocash::convertBytesVectorToVector(obj_bv, commitment_bv);
tree->insertElement(commitment_bv, new_index);
if (!witnessed) {
index = new_index;
}
}
OldIncrementalMerkleTree witness() {
OldIncrementalMerkleTree ret;
ret.tree->setTo(*tree);
ret.index = index;
ret.witnessed = true;
return ret;
}
};
template<typename A, typename B, typename C>
void expect_ser_test_vector(B& b, const C& c, const A& tree) {
expect_test_vector<B, C>(b, c);
}
template<typename B, typename C>
void expect_ser_test_vector(B& b, const C& c, const OldIncrementalMerkleTree& tree) {
// Don't perform serialization tests on the old tree.
}
template<typename Tree, typename Witness>
void test_tree(Array root_tests, Array ser_tests, Array witness_ser_tests, Array path_tests) {
Array::iterator root_iterator = root_tests.begin();
@ -250,13 +153,7 @@ void test_tree(Array root_tests, Array ser_tests, Array witness_ser_tests, Array
} else {
auto path = wit.path();
// The old tree has some serious bugs which make it
// fail some of these test vectors.
//
// The new tree is strictly more correct in its
// behavior, as we demonstrate by constructing and
// evaluating the tree over a dummy circuit.
if (typeid(Tree) != typeid(OldIncrementalMerkleTree)) {
{
expect_test_vector(path_iterator, path);
typedef Fr<default_r1cs_ppzksnark_pp> FieldT;
@ -316,8 +213,7 @@ void test_tree(Array root_tests, Array ser_tests, Array witness_ser_tests, Array
}
}
// The old tree would silently ignore appending when it was full.
if (typeid(Tree) != typeid(OldIncrementalMerkleTree)) {
{
// Tree should be full now
ASSERT_THROW(tree.append(uint256()), std::runtime_error);
@ -337,7 +233,6 @@ TEST(merkletree, vectors) {
Array path_tests = read_json(std::string(json_tests::merkle_path, json_tests::merkle_path + sizeof(json_tests::merkle_path)));
test_tree<ZCTestingIncrementalMerkleTree, ZCTestingIncrementalWitness>(root_tests, ser_tests, witness_ser_tests, path_tests);
test_tree<OldIncrementalMerkleTree, OldIncrementalMerkleTree>(root_tests, ser_tests, witness_ser_tests, path_tests);
}
TEST(merkletree, deserializeInvalid) {

View File

@ -5,6 +5,7 @@
#include "zcash/NoteEncryption.hpp"
#include "zcash/prf.h"
#include "crypto/sha256.h"
class TestNoteDecryption : public ZCNoteDecryption {
public:
@ -28,8 +29,8 @@ TEST(noteencryption, api)
ASSERT_TRUE(b.get_epk() != c.get_epk());
}
boost::array<unsigned char, 216> message;
for (unsigned char i = 0; i < 216; i++) {
boost::array<unsigned char, 201> message;
for (unsigned char i = 0; i < 201; i++) {
// Fill the message with dummy data
message[i] = (unsigned char) i;
}

View File

@ -52,7 +52,7 @@
using namespace std;
libzerocash::ZerocashParams *pzerocashParams = NULL;
ZCJoinSplit* pzcashParams = NULL;
#ifdef ENABLE_WALLET
CWallet* pwalletMain = NULL;
@ -602,27 +602,21 @@ static void ZC_LoadParams()
struct timeval tv_start, tv_end;
float elapsed;
boost::filesystem::path pk_path = ZC_GetParamsDir() / "zc-testnet-public-alpha-proving.key";
boost::filesystem::path vk_path = ZC_GetParamsDir() / "zc-testnet-public-alpha-verification.key";
libzerocash::ZerocashParams::zerocash_pp::init_public_params();
boost::filesystem::path pk_path = ZC_GetParamsDir() / "z3-proving.key";
boost::filesystem::path vk_path = ZC_GetParamsDir() / "z3-verification.key";
pzcashParams = ZCJoinSplit::Unopened();
LogPrintf("Loading verification key from %s\n", vk_path.string().c_str());
gettimeofday(&tv_start, 0);
auto vk_loaded = libzerocash::ZerocashParams::LoadVerificationKeyFromFile(
vk_path.string(),
INCREMENTAL_MERKLE_TREE_DEPTH
);
pzcashParams->loadVerifyingKey(vk_path.string());
gettimeofday(&tv_end, 0);
elapsed = float(tv_end.tv_sec-tv_start.tv_sec) + (tv_end.tv_usec-tv_start.tv_usec)/float(1000000);
LogPrintf("Loaded verification key in %fs seconds.\n", elapsed);
pzerocashParams = new libzerocash::ZerocashParams(
INCREMENTAL_MERKLE_TREE_DEPTH,
pk_path.string(),
&vk_loaded
);
pzcashParams->setProvingKeyPath(pk_path.string());
}
/** Initialize bitcoin.

View File

@ -8,7 +8,7 @@
#include <string>
#include "zerocash/ZerocashParams.h"
#include "zcash/JoinSplit.hpp"
class CScheduler;
class CWallet;
@ -19,7 +19,7 @@ class thread_group;
} // namespace boost
extern CWallet* pwalletMain;
extern libzerocash::ZerocashParams* pzerocashParams;
extern ZCJoinSplit* pzcashParams;
void StartShutdown();
bool ShutdownRequested();

View File

@ -960,7 +960,10 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
if (state.PerformPourVerification()) {
BOOST_FOREACH(const CPourTx &pour, tx.vpour) {
if (!pour.Verify(*pzerocashParams)) {
// TODO: #808
uint256 pubKeyHash;
if (!pour.Verify(*pzcashParams, pubKeyHash)) {
return state.DoS(100, error("CheckTransaction(): pour does not verify"),
REJECT_INVALID, "bad-txns-pour-verification-failed");
}

View File

@ -9,84 +9,56 @@
#include "tinyformat.h"
#include "utilstrencodings.h"
#include "zerocash/PourProver.h"
#include "zerocash/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,
CPourTx::CPourTx(ZCJoinSplit& params,
const uint256& pubKeyHash,
const uint256& anchor,
const boost::array<PourInput, NUM_POUR_INPUTS>& inputs,
const boost::array<PourOutput, NUM_POUR_OUTPUTS>& outputs,
const boost::array<libzcash::JSInput, ZC_NUM_JS_INPUTS>& inputs,
const boost::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS>& outputs,
CAmount vpub_old,
CAmount vpub_new) : scriptSig(), scriptPubKey(scriptPubKey), vpub_old(vpub_old), vpub_new(vpub_new), anchor(anchor)
CAmount vpub_new) : vpub_old(vpub_old), vpub_new(vpub_new), anchor(anchor)
{
uint256 scriptPubKeyHash;
{
CHashWriter ss(SER_GETHASH, 0);
ss << scriptPubKey;
scriptPubKeyHash = ss.GetHash();
}
boost::array<libzcash::Note, ZC_NUM_JS_OUTPUTS> notes;
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;
proof = pourtx.unpack(serials_bv, commitments_bv, macs_bv, ciphertexts, ephemeralKey);
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);
}
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()),
params.loadProvingKey();
proof = params.prove(
inputs,
outputs,
notes,
ciphertexts,
ephemeralKey,
pubKeyHash,
randomSeed,
macs,
serials,
commitments,
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
anchor
);
}
bool CPourTx::Verify(
ZCJoinSplit& params,
const uint256& pubKeyHash
) const {
return params.verify(
proof,
pubKeyHash,
randomSeed,
macs,
serials,
commitments,
vpub_old,
vpub_new,
anchor
);
}
uint256 CPourTx::h_sig(ZCJoinSplit& params, const uint256& pubKeyHash) const
{
return params.h_sig(randomSeed, serials, pubKeyHash);
}
std::string COutPoint::ToString() const
{
return strprintf("COutPoint(%s, %u)", hash.ToString().substr(0,10), n);

View File

@ -13,16 +13,9 @@
#include <boost/array.hpp>
#include "zerocash/ZerocashParams.h"
#include "zerocash/PourInput.h"
#include "zerocash/PourOutput.h"
#include "zcash/NoteEncryption.hpp"
using namespace libzerocash;
static const unsigned int NUM_POUR_INPUTS = 2;
static const unsigned int NUM_POUR_OUTPUTS = 2;
#include "zcash/Zcash.h"
#include "zcash/JoinSplit.hpp"
class CPourTx
{
@ -32,14 +25,6 @@ public:
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
@ -50,28 +35,31 @@ public:
// are derived from the secrets placed in the bucket
// and the secret spend-authority key known by the
// spender.
boost::array<uint256, NUM_POUR_INPUTS> serials;
boost::array<uint256, ZC_NUM_JS_INPUTS> 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, NUM_POUR_OUTPUTS> commitments;
boost::array<uint256, ZC_NUM_JS_OUTPUTS> commitments;
// Ciphertexts
// These contain trapdoors, values and other information
// that the recipient needs, including a memo field. It
// is encrypted using the scheme implemented in crypto/NoteEncryption.cpp
boost::array<ZCNoteEncryption::Ciphertext, NUM_POUR_OUTPUTS> ciphertexts;
boost::array<ZCNoteEncryption::Ciphertext, ZC_NUM_JS_OUTPUTS> ciphertexts;
// Ephemeral key
uint256 ephemeralKey;
// Random seed
uint256 randomSeed;
// MACs
// The verification of the pour requires these MACs
// to be provided as an input.
boost::array<uint256, NUM_POUR_INPUTS> macs;
boost::array<uint256, ZC_NUM_JS_INPUTS> macs;
// Pour proof
// This is a zk-SNARK which ensures that this pour is valid.
@ -79,17 +67,20 @@ public:
CPourTx(): vpub_old(0), vpub_new(0) { }
CPourTx(ZerocashParams& params,
const CScript& scriptPubKey,
CPourTx(ZCJoinSplit& params,
const uint256& pubKeyHash,
const uint256& rt,
const boost::array<PourInput, NUM_POUR_INPUTS>& inputs,
const boost::array<PourOutput, NUM_POUR_OUTPUTS>& outputs,
const boost::array<libzcash::JSInput, ZC_NUM_JS_INPUTS>& inputs,
const boost::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS>& outputs,
CAmount vpub_old,
CAmount vpub_new
);
// Verifies that the pour proof is correct.
bool Verify(ZerocashParams& params) const;
bool Verify(ZCJoinSplit& params, const uint256& pubKeyHash) const;
// Returns the calculated h_sig
uint256 h_sig(ZCJoinSplit& params, const uint256& pubKeyHash) const;
ADD_SERIALIZE_METHODS;
@ -97,13 +88,12 @@ public:
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(ephemeralKey);
READWRITE(randomSeed);
READWRITE(macs);
READWRITE(proof);
}
@ -113,13 +103,12 @@ public:
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.ephemeralKey == b.ephemeralKey &&
a.randomSeed == b.randomSeed &&
a.macs == b.macs &&
a.proof == b.proof
);

View File

@ -124,19 +124,9 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry)
pour.push_back(Pair("vpub_old", ValueFromAmount(pourtx.vpub_old)));
pour.push_back(Pair("vpub_new", ValueFromAmount(pourtx.vpub_new)));
{
Object o;
ScriptPubKeyToJSON(pourtx.scriptPubKey, o, true);
pour.push_back(Pair("scriptPubKey", o));
}
{
Object o;
ScriptPubKeyToJSON(pourtx.scriptSig, o, true);
pour.push_back(Pair("scriptSig", o));
}
pour.push_back(Pair("valid", pourtx.Verify(*pzerocashParams)));
// TODO: #808
uint256 pubKeyHash;
pour.push_back(Pair("valid", pourtx.Verify(*pzcashParams, pubKeyHash)));
vpour.push_back(pour);
}

View File

@ -166,10 +166,12 @@ BOOST_AUTO_TEST_CASE(serials_test)
void appendRandomCommitment(ZCIncrementalMerkleTree &tree)
{
Address addr = Address::CreateNewRandomAddress();
Coin coin(addr.getPublicAddress(), 100);
libzcash::SpendingKey k = libzcash::SpendingKey::random();
libzcash::PaymentAddress addr = k.address();
tree.append(uint256(coin.getCoinCommitment().getCommitmentValue()));
libzcash::Note note(addr.a_pk, 0, uint256(), uint256());
tree.append(note.cm());
}
BOOST_AUTO_TEST_CASE(anchors_flush_test)

File diff suppressed because one or more lines are too long

View File

@ -124,12 +124,12 @@ void static RandomTransaction(CMutableTransaction &tx, bool fSingle) {
} else {
pourtx.vpub_new = insecure_rand() % 100000000;
}
RandomScript(pourtx.scriptPubKey);
RandomScript(pourtx.scriptSig);
pourtx.anchor = GetRandHash();
pourtx.serials[0] = GetRandHash();
pourtx.serials[1] = GetRandHash();
pourtx.ephemeralKey = GetRandHash();
pourtx.randomSeed = GetRandHash();
pourtx.ciphertexts[0] = {insecure_rand() % 100, insecure_rand() % 100};
pourtx.ciphertexts[1] = {insecure_rand() % 100, insecure_rand() % 100};
pourtx.macs[0] = GetRandHash();

View File

@ -25,7 +25,7 @@
CClientUIInterface uiInterface; // Declared but not defined in ui_interface.h
CWallet* pwalletMain;
libzerocash::ZerocashParams *pzerocashParams;
ZCJoinSplit *pzcashParams;
extern bool fPrintToConsole;
extern void noui_connect();

View File

@ -25,15 +25,11 @@
#include <boost/assign/list_of.hpp>
#include "json/json_spirit_writer_template.h"
#include "zerocash/ZerocashParams.h"
#include "zerocash/PourInput.h"
#include "zerocash/PourOutput.h"
#include "zerocash/Address.h"
#include "zerocash/Coin.h"
#include "zcash/Note.hpp"
#include "zcash/Address.hpp"
using namespace std;
using namespace json_spirit;
using namespace libzerocash;
// In script_tests.cpp
extern Array read_json(const std::string& jsondata);
@ -311,19 +307,18 @@ BOOST_AUTO_TEST_CASE(test_basic_pour_verification)
// the integrity of the scheme through its own tests.
// construct the r1cs keypair
auto keypair = ZerocashParams::GenerateNewKeyPair(INCREMENTAL_MERKLE_TREE_DEPTH);
ZerocashParams p(
INCREMENTAL_MERKLE_TREE_DEPTH,
&keypair
);
auto p = ZCJoinSplit::Generate();
// construct a merkle tree
ZCIncrementalMerkleTree merkleTree;
Address addr = Address::CreateNewRandomAddress();
Coin coin(addr.getPublicAddress(), 100);
libzcash::SpendingKey k = libzcash::SpendingKey::random();
libzcash::PaymentAddress addr = k.address();
libzcash::Note note(addr.a_pk, 100, uint256(), uint256());
// commitment from coin
uint256 commitment(coin.getCoinCommitment().getCommitmentValue());
uint256 commitment = note.cm();
// insert commitment into the merkle tree
merkleTree.append(commitment);
@ -332,22 +327,21 @@ BOOST_AUTO_TEST_CASE(test_basic_pour_verification)
uint256 rt = merkleTree.root();
auto witness = merkleTree.witness();
auto path = witness.path();
// create CPourTx
CScript scriptPubKey;
boost::array<PourInput, NUM_POUR_INPUTS> inputs = {
PourInput(coin, addr, path),
PourInput(INCREMENTAL_MERKLE_TREE_DEPTH) // dummy input of zero value
uint256 pubKeyHash;
boost::array<libzcash::JSInput, ZC_NUM_JS_INPUTS> inputs = {
libzcash::JSInput(witness, note, k),
libzcash::JSInput() // dummy input of zero value
};
boost::array<PourOutput, NUM_POUR_OUTPUTS> outputs = {
PourOutput(50),
PourOutput(50)
boost::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS> outputs = {
libzcash::JSOutput(addr, 50),
libzcash::JSOutput(addr, 50)
};
{
CPourTx pourtx(p, scriptPubKey, uint256(rt), inputs, outputs, 0, 0);
BOOST_CHECK(pourtx.Verify(p));
CPourTx pourtx(*p, pubKeyHash, rt, inputs, outputs, 0, 0);
BOOST_CHECK(pourtx.Verify(*p, pubKeyHash));
CDataStream ss(SER_DISK, CLIENT_VERSION);
ss << pourtx;
@ -356,21 +350,23 @@ BOOST_AUTO_TEST_CASE(test_basic_pour_verification)
ss >> pourtx_deserialized;
BOOST_CHECK(pourtx_deserialized == pourtx);
BOOST_CHECK(pourtx_deserialized.Verify(p));
BOOST_CHECK(pourtx_deserialized.Verify(*p, pubKeyHash));
}
{
// 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);
BOOST_CHECK_THROW(CPourTx(*p, pubKeyHash, rt, inputs, outputs, 10, 0), std::invalid_argument);
BOOST_CHECK_THROW(CPourTx(*p, pubKeyHash, 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);
auto test = CPourTx(*p, pubKeyHash, rt, inputs, outputs, 0, 0);
test.anchor = GetRandHash();
BOOST_CHECK(!test.Verify(p));
BOOST_CHECK(!test.Verify(*p, pubKeyHash));
}
delete p;
}
BOOST_AUTO_TEST_CASE(test_simple_pour_invalidity)

View File

@ -29,6 +29,8 @@
using namespace std;
using namespace json_spirit;
using namespace libzcash;
int64_t nWalletUnlockTime;
static CCriticalSection cs_nWalletUnlockTime;
@ -2385,7 +2387,7 @@ Value zc_benchmark(const json_spirit::Array& params, bool fHelp)
if (benchmarktype == "createjoinsplit") {
/* Load the proving now key so that it doesn't happen as part of the
* first joinsplit. */
pzerocashParams->loadProvingKey();
pzcashParams->loadProvingKey();
}
for (int i = 0; i < samplecount; i++) {
@ -2454,14 +2456,12 @@ Value zc_raw_receive(const json_spirit::Array& params, bool fHelp)
LOCK(cs_main);
uint256 a_sk;
uint256 sk_enc;
SpendingKey k;
{
CDataStream ssData(ParseHexV(params[0], "zcsecretkey"), SER_NETWORK, PROTOCOL_VERSION);
try {
ssData >> a_sk;
ssData >> sk_enc;
ssData >> k;
} catch(const std::exception &) {
throw runtime_error(
"zcsecretkey could not be decoded"
@ -2469,12 +2469,10 @@ Value zc_raw_receive(const json_spirit::Array& params, bool fHelp)
}
}
libzerocash::PrivateAddress zcsecretkey(a_sk, sk_enc);
libzerocash::Address zcaddress(zcsecretkey);
uint256 epk;
unsigned char nonce;
ZCNoteEncryption::Ciphertext ct;
uint256 h_sig;
{
CDataStream ssData(ParseHexV(params[1], "encrypted_bucket"), SER_NETWORK, PROTOCOL_VERSION);
@ -2482,6 +2480,7 @@ Value zc_raw_receive(const json_spirit::Array& params, bool fHelp)
ssData >> nonce;
ssData >> epk;
ssData >> ct;
ssData >> h_sig;
} catch(const std::exception &) {
throw runtime_error(
"encrypted_bucket could not be decoded"
@ -2489,32 +2488,40 @@ Value zc_raw_receive(const json_spirit::Array& params, bool fHelp)
}
}
libzerocash::Coin decrypted_bucket(ct, zcaddress, epk, nonce);
ZCNoteDecryption decryptor(k.viewing_key());
std::vector<unsigned char> commitment_v = decrypted_bucket.getCoinCommitment().getCommitmentValue();
uint256 commitment = uint256(commitment_v);
NotePlaintext npt = NotePlaintext::decrypt(
decryptor,
ct,
epk,
h_sig,
nonce
);
PaymentAddress payment_addr = k.address();
Note decrypted_note = npt.note(payment_addr);
assert(pwalletMain != NULL);
libzcash::MerklePath path;
std::vector<boost::optional<ZCIncrementalWitness>> witnesses;
uint256 anchor;
auto found_in_chain = pwalletMain->WitnessBucketCommitment(commitment, path, anchor);
CAmount value_of_bucket = decrypted_bucket.getValue();
uint256 commitment = decrypted_note.cm();
pwalletMain->WitnessBucketCommitment(
{commitment},
witnesses,
anchor
);
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
{
ss << decrypted_bucket.getValue();
ss << decrypted_bucket.getRho();
ss << decrypted_bucket.getR();
}
ss << npt;
Object result;
result.push_back(Pair("amount", ValueFromAmount(value_of_bucket)));
result.push_back(Pair("amount", ValueFromAmount(decrypted_note.value)));
result.push_back(Pair("bucket", HexStr(ss.begin(), ss.end())));
result.push_back(Pair("exists", found_in_chain));
result.push_back(Pair("exists", (bool) witnesses[0]));
return result;
}
Value zc_raw_pour(const json_spirit::Array& params, bool fHelp)
{
if (!EnsureWalletIsAvailable(fHelp)) {
@ -2563,30 +2570,20 @@ Value zc_raw_pour(const json_spirit::Array& params, bool fHelp)
if (params[4].get_real() != 0.0)
vpub_new = AmountFromValue(params[4]);
std::vector<PourInput> vpourin;
std::vector<PourOutput> vpourout;
uint256 anchor;
std::vector<JSInput> vpourin;
std::vector<JSOutput> vpourout;
std::vector<Note> notes;
std::vector<SpendingKey> keys;
std::vector<uint256> commitments;
BOOST_FOREACH(const Pair& s, inputs)
{
CDataStream ssData(ParseHexV(s.name_, "bucket"), SER_NETWORK, PROTOCOL_VERSION);
uint64_t value;
std::vector<unsigned char> rho;
std::vector<unsigned char> r;
ssData >> value;
ssData >> rho;
ssData >> r;
uint256 a_sk;
uint256 sk_enc;
SpendingKey k;
{
CDataStream ssData2(ParseHexV(s.value_, "zcsecretkey"), SER_NETWORK, PROTOCOL_VERSION);
CDataStream ssData(ParseHexV(s.value_, "zcsecretkey"), SER_NETWORK, PROTOCOL_VERSION);
try {
ssData2 >> a_sk;
ssData2 >> sk_enc;
ssData >> k;
} catch(const std::exception &) {
throw runtime_error(
"zcsecretkey could not be decoded"
@ -2594,68 +2591,76 @@ Value zc_raw_pour(const json_spirit::Array& params, bool fHelp)
}
}
libzerocash::PrivateAddress zcsecretkey(a_sk, sk_enc);
libzerocash::Address zcaddress(zcsecretkey);
libzerocash::Coin input_coin(zcaddress.getPublicAddress(), value, rho, r);
keys.push_back(k);
std::vector<unsigned char> commitment_v = input_coin.getCoinCommitment().getCommitmentValue();
uint256 commitment = uint256(commitment_v);
NotePlaintext npt;
libzcash::MerklePath path;
assert(pwalletMain != NULL);
if (!pwalletMain->WitnessBucketCommitment(commitment, path, anchor)) {
throw std::runtime_error("Couldn't find bucket in the blockchain");
{
CDataStream ssData(ParseHexV(s.name_, "bucket"), SER_NETWORK, PROTOCOL_VERSION);
ssData >> npt;
}
vpourin.push_back(PourInput(input_coin, zcaddress, path));
PaymentAddress addr = k.address();
Note note = npt.note(addr);
notes.push_back(note);
commitments.push_back(note.cm());
}
while (vpourin.size() < NUM_POUR_INPUTS) {
vpourin.push_back(PourInput(INCREMENTAL_MERKLE_TREE_DEPTH));
uint256 anchor;
std::vector<boost::optional<ZCIncrementalWitness>> witnesses;
pwalletMain->WitnessBucketCommitment(commitments, witnesses, anchor);
assert(witnesses.size() == notes.size());
assert(notes.size() == keys.size());
{
for (size_t i = 0; i < witnesses.size(); i++) {
if (!witnesses[i]) {
throw runtime_error(
"pour input could not be found in tree"
);
}
vpourin.push_back(JSInput(*witnesses[i], notes[i], keys[i]));
}
}
while (vpourin.size() < ZC_NUM_JS_INPUTS) {
vpourin.push_back(JSInput());
}
BOOST_FOREACH(const Pair& s, outputs)
{
libzerocash::PublicAddress addrTo;
PaymentAddress addrTo;
{
CDataStream ssData(ParseHexV(s.name_, "to_address"), SER_NETWORK, PROTOCOL_VERSION);
uint256 pubAddressSecret;
uint256 encryptionPublicKey;
ssData >> pubAddressSecret;
ssData >> encryptionPublicKey;
addrTo = libzerocash::PublicAddress(pubAddressSecret, encryptionPublicKey);
ssData >> addrTo;
}
CAmount nAmount = AmountFromValue(s.value_);
libzerocash::Coin coin(addrTo, nAmount);
libzerocash::PourOutput output(coin, addrTo);
vpourout.push_back(output);
vpourout.push_back(JSOutput(addrTo, nAmount));
}
while (vpourout.size() < NUM_POUR_OUTPUTS) {
vpourout.push_back(PourOutput(0));
while (vpourout.size() < ZC_NUM_JS_OUTPUTS) {
vpourout.push_back(JSOutput());
}
// TODO
if (vpourout.size() != NUM_POUR_INPUTS || vpourin.size() != NUM_POUR_OUTPUTS) {
if (vpourout.size() != ZC_NUM_JS_INPUTS || vpourin.size() != ZC_NUM_JS_OUTPUTS) {
throw runtime_error("unsupported pour input/output counts");
}
CScript scriptPubKey;
CPourTx pourtx(*pzerocashParams,
scriptPubKey,
// TODO: #808
uint256 pubKeyHash;
CPourTx pourtx(*pzcashParams,
pubKeyHash,
anchor,
{vpourin[0], vpourin[1]},
{vpourout[0], vpourout[1]},
vpub_old,
vpub_new);
assert(pourtx.Verify(*pzerocashParams));
assert(pourtx.Verify(*pzcashParams, pubKeyHash));
CMutableTransaction mtx(tx);
mtx.nVersion = 2;
@ -2673,6 +2678,7 @@ Value zc_raw_pour(const json_spirit::Array& params, bool fHelp)
ss2 << ((unsigned char) 0x00);
ss2 << pourtx.ephemeralKey;
ss2 << pourtx.ciphertexts[0];
ss2 << pourtx.h_sig(*pzcashParams, pubKeyHash);
encryptedBucket1 = HexStr(ss2.begin(), ss2.end());
}
@ -2681,6 +2687,7 @@ Value zc_raw_pour(const json_spirit::Array& params, bool fHelp)
ss2 << ((unsigned char) 0x01);
ss2 << pourtx.ephemeralKey;
ss2 << pourtx.ciphertexts[1];
ss2 << pourtx.h_sig(*pzcashParams, pubKeyHash);
encryptedBucket2 = HexStr(ss2.begin(), ss2.end());
}
@ -2711,22 +2718,25 @@ Value zc_raw_keygen(const json_spirit::Array& params, bool fHelp)
);
}
auto zckeypair = libzerocash::Address::CreateNewRandomAddress();
auto k = SpendingKey::random();
auto addr = k.address();
auto viewing_key = k.viewing_key();
CDataStream pub(SER_NETWORK, PROTOCOL_VERSION);
CDataStream priv(SER_NETWORK, PROTOCOL_VERSION);
CDataStream viewing(SER_NETWORK, PROTOCOL_VERSION);
pub << zckeypair.getPublicAddress().getPublicAddressSecret(); // a_pk
pub << zckeypair.getPublicAddress().getEncryptionPublicKey(); // pk_enc
priv << zckeypair.getPrivateAddress().getAddressSecret(); // a_sk
priv << zckeypair.getPrivateAddress().getEncryptionSecretKey(); // sk_enc
pub << addr;
priv << k;
viewing << viewing_key;
std::string pub_hex = HexStr(pub.begin(), pub.end());
std::string priv_hex = HexStr(priv.begin(), priv.end());
std::string viewing_hex = HexStr(viewing.begin(), viewing.end());
Object result;
result.push_back(Pair("zcaddress", pub_hex));
result.push_back(Pair("zcsecretkey", priv_hex));
result.push_back(Pair("zcviewingkey", viewing_hex));
return result;
}

View File

@ -1051,14 +1051,13 @@ bool CWalletTx::WriteToDisk(CWalletDB *pwalletdb)
return pwalletdb->WriteTx(GetHash(), *this);
}
bool CWallet::WitnessBucketCommitment(uint256 &commitment,
libzcash::MerklePath &path,
void CWallet::WitnessBucketCommitment(std::vector<uint256> commitments,
std::vector<boost::optional<ZCIncrementalWitness>>& witnesses,
uint256 &final_anchor)
{
witnesses.resize(commitments.size());
CBlockIndex* pindex = chainActive.Genesis();
ZCIncrementalMerkleTree tree;
boost::optional<ZCIncrementalWitness> witness = boost::none;
uint256 current_anchor;
while (pindex) {
CBlock block;
@ -1070,24 +1069,26 @@ bool CWallet::WitnessBucketCommitment(uint256 &commitment,
{
BOOST_FOREACH(const uint256 &bucket_commitment, pour.commitments)
{
if (witness) {
witness->append(bucket_commitment);
} else {
tree.append(bucket_commitment);
tree.append(bucket_commitment);
if (bucket_commitment == commitment) {
witness = tree.witness();
BOOST_FOREACH(boost::optional<ZCIncrementalWitness>& wit, witnesses) {
if (wit) {
wit->append(bucket_commitment);
}
}
size_t i = 0;
BOOST_FOREACH(uint256& commitment, commitments) {
if (bucket_commitment == commitment) {
witnesses.at(i) = tree.witness();
}
i++;
}
}
}
}
if (witness) {
current_anchor = witness->root();
} else {
current_anchor = tree.root();
}
uint256 current_anchor = tree.root();
// Consistency check: we should be able to find the current tree
// in our CCoins view.
@ -1097,14 +1098,14 @@ bool CWallet::WitnessBucketCommitment(uint256 &commitment,
pindex = chainActive.Next(pindex);
}
if (witness) {
path = witness->path();
final_anchor = current_anchor;
// TODO: #93; Select a root via some heuristic.
final_anchor = tree.root();
return true;
BOOST_FOREACH(boost::optional<ZCIncrementalWitness>& wit, witnesses) {
if (wit) {
assert(final_anchor == wit->root());
}
}
return false;
}
/**

View File

@ -616,7 +616,10 @@ public:
void SyncTransaction(const CTransaction& tx, const CBlock* pblock);
bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate);
void EraseFromWallet(const uint256 &hash);
bool WitnessBucketCommitment(uint256 &commitment, libzcash::MerklePath& path, uint256 &final_anchor);
void WitnessBucketCommitment(
std::vector<uint256> commitments,
std::vector<boost::optional<ZCIncrementalWitness>>& witnesses,
uint256 &final_anchor);
int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false);
void ReacceptWalletTransactions();
void ResendWalletTransactions(int64_t nBestBlockTime);

23
src/zcash/Address.cpp Normal file
View File

@ -0,0 +1,23 @@
#include "Address.hpp"
#include "NoteEncryption.hpp"
#include "prf.h"
namespace libzcash {
uint256 ViewingKey::pk_enc() {
return ZCNoteEncryption::generate_pubkey(*this);
}
ViewingKey SpendingKey::viewing_key() {
return ViewingKey(ZCNoteEncryption::generate_privkey(*this));
}
SpendingKey SpendingKey::random() {
return SpendingKey(random_uint256());
}
PaymentAddress SpendingKey::address() {
return PaymentAddress(PRF_addr_a_pk(*this), viewing_key().pk_enc());
}
}

53
src/zcash/Address.hpp Normal file
View File

@ -0,0 +1,53 @@
#ifndef _ZCADDRESS_H_
#define _ZCADDRESS_H_
#include "uint256.h"
#include "serialize.h"
namespace libzcash {
class PaymentAddress {
public:
uint256 a_pk;
uint256 pk_enc;
PaymentAddress() : a_pk(), pk_enc() { }
PaymentAddress(uint256 a_pk, uint256 pk_enc) : a_pk(a_pk), pk_enc(pk_enc) { }
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
unsigned char leadingByte = 0x92;
READWRITE(leadingByte);
if (leadingByte != 0x92) {
throw std::ios_base::failure("unrecognized payment address lead byte");
}
READWRITE(a_pk);
READWRITE(pk_enc);
}
};
class ViewingKey : public uint256 {
public:
ViewingKey(uint256 sk_enc) : uint256(sk_enc) { }
uint256 pk_enc();
};
class SpendingKey : public uint256 {
public:
SpendingKey() : uint256() { }
SpendingKey(uint256 a_sk) : uint256(a_sk) { }
static SpendingKey random();
ViewingKey viewing_key();
PaymentAddress address();
};
}
#endif // _ZCADDRESS_H_

View File

@ -0,0 +1,21 @@
#include "zcash/JoinSplit.hpp"
#include <iostream>
int main(int argc, char **argv)
{
if(argc != 3) {
std::cerr << "Usage: " << argv[0] << " provingKeyFileName verificationKeyFileName" << std::endl;
return 1;
}
std::string pkFile = argv[1];
std::string vkFile = argv[2];
auto p = ZCJoinSplit::Generate();
p->saveProvingKey(pkFile);
p->saveVerifyingKey(vkFile);
return 0;
}

View File

@ -8,8 +8,7 @@
#include "uint256.h"
#include "serialize.h"
static const unsigned int INCREMENTAL_MERKLE_TREE_DEPTH = 20;
static const unsigned int INCREMENTAL_MERKLE_TREE_DEPTH_TESTING = 4;
#include "Zcash.h"
namespace libzcash {

345
src/zcash/JoinSplit.cpp Normal file
View File

@ -0,0 +1,345 @@
#include "JoinSplit.hpp"
#include "prf.h"
#include "sodium.h"
#include "zerocash/utils/util.h"
#include "zcash/util.h"
#include <memory>
#include <boost/foreach.hpp>
#include <boost/format.hpp>
#include <boost/optional.hpp>
#include <fstream>
#include "libsnark/common/default_types/r1cs_ppzksnark_pp.hpp"
#include "libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp"
#include "libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_gadget.hpp"
#include "libsnark/gadgetlib1/gadgets/merkle_tree/merkle_tree_check_read_gadget.hpp"
#include "sync.h"
using namespace libsnark;
namespace libzcash {
#include "zcash/circuit/gadget.tcc"
CCriticalSection cs_ParamsIO;
CCriticalSection cs_InitializeParams;
template<typename T>
void saveToFile(std::string path, T& obj) {
LOCK(cs_ParamsIO);
std::stringstream ss;
ss << obj;
std::ofstream fh;
fh.open(path, std::ios::binary);
ss.rdbuf()->pubseekpos(0, std::ios_base::out);
fh << ss.rdbuf();
fh.flush();
fh.close();
}
template<typename T>
void loadFromFile(std::string path, boost::optional<T>& objIn) {
LOCK(cs_ParamsIO);
std::stringstream ss;
std::ifstream fh(path, std::ios::binary);
if(!fh.is_open()) {
throw std::runtime_error((boost::format("could not load param file at %s") % path).str());
}
ss << fh.rdbuf();
fh.close();
ss.rdbuf()->pubseekpos(0, std::ios_base::in);
T obj;
ss >> obj;
objIn = std::move(obj);
}
template<size_t NumInputs, size_t NumOutputs>
class JoinSplitCircuit : public JoinSplit<NumInputs, NumOutputs> {
public:
typedef default_r1cs_ppzksnark_pp ppzksnark_ppT;
typedef Fr<ppzksnark_ppT> FieldT;
boost::optional<r1cs_ppzksnark_proving_key<ppzksnark_ppT>> pk;
boost::optional<r1cs_ppzksnark_verification_key<ppzksnark_ppT>> vk;
boost::optional<std::string> pkPath;
static void initialize() {
LOCK(cs_InitializeParams);
ppzksnark_ppT::init_public_params();
}
void setProvingKeyPath(std::string path) {
pkPath = path;
}
void loadProvingKey() {
if (!pk) {
if (!pkPath) {
throw std::runtime_error("proving key path unknown");
}
loadFromFile(*pkPath, pk);
}
}
void saveProvingKey(std::string path) {
if (pk) {
saveToFile(path, *pk);
} else {
throw std::runtime_error("cannot save proving key; key doesn't exist");
}
}
void loadVerifyingKey(std::string path) {
loadFromFile(path, vk);
}
void saveVerifyingKey(std::string path) {
if (vk) {
saveToFile(path, *vk);
} else {
throw std::runtime_error("cannot save verifying key; key doesn't exist");
}
}
void generate() {
protoboard<FieldT> pb;
joinsplit_gadget<FieldT, NumInputs, NumOutputs> g(pb);
g.generate_r1cs_constraints();
const r1cs_constraint_system<FieldT> constraint_system = pb.get_constraint_system();
r1cs_ppzksnark_keypair<ppzksnark_ppT> keypair = r1cs_ppzksnark_generator<ppzksnark_ppT>(constraint_system);
pk = keypair.pk;
vk = keypair.vk;
}
JoinSplitCircuit() {}
bool verify(
const std::string& proof,
const uint256& pubKeyHash,
const uint256& randomSeed,
const boost::array<uint256, NumInputs>& macs,
const boost::array<uint256, NumInputs>& nullifiers,
const boost::array<uint256, NumOutputs>& commitments,
uint64_t vpub_old,
uint64_t vpub_new,
const uint256& rt
) {
if (!vk) {
throw std::runtime_error("JoinSplit verifying key not loaded");
}
r1cs_ppzksnark_proof<ppzksnark_ppT> r1cs_proof;
std::stringstream ss;
ss.str(proof);
ss >> r1cs_proof;
uint256 h_sig = this->h_sig(randomSeed, nullifiers, pubKeyHash);
auto witness = joinsplit_gadget<FieldT, NumInputs, NumOutputs>::witness_map(
rt,
h_sig,
macs,
nullifiers,
commitments,
vpub_old,
vpub_new
);
return r1cs_ppzksnark_verifier_strong_IC<ppzksnark_ppT>(*vk, witness, r1cs_proof);
}
std::string prove(
const boost::array<JSInput, NumInputs>& inputs,
const boost::array<JSOutput, NumOutputs>& outputs,
boost::array<Note, NumOutputs>& out_notes,
boost::array<ZCNoteEncryption::Ciphertext, NumOutputs>& out_ciphertexts,
uint256& out_ephemeralKey,
const uint256& pubKeyHash,
uint256& out_randomSeed,
boost::array<uint256, NumInputs>& out_macs,
boost::array<uint256, NumInputs>& out_nullifiers,
boost::array<uint256, NumOutputs>& out_commitments,
uint64_t vpub_old,
uint64_t vpub_new,
const uint256& rt
) {
if (!pk) {
throw std::runtime_error("JoinSplit proving key not loaded");
}
// Compute nullifiers of inputs
for (size_t i = 0; i < NumInputs; i++) {
out_nullifiers[i] = inputs[i].nullifier();
}
// Sample randomSeed
out_randomSeed = random_uint256();
// Compute h_sig
uint256 h_sig = this->h_sig(out_randomSeed, out_nullifiers, pubKeyHash);
// Sample phi
uint256 phi = random_uint256();
// Compute notes for outputs
for (size_t i = 0; i < NumOutputs; i++) {
// Sample r
uint256 r = random_uint256();
out_notes[i] = outputs[i].note(phi, r, i, h_sig);
}
// Compute the output commitments
for (size_t i = 0; i < NumOutputs; i++) {
out_commitments[i] = out_notes[i].cm();
}
// Encrypt the ciphertexts containing the note
// plaintexts to the recipients of the value.
{
ZCNoteEncryption encryptor(h_sig);
for (size_t i = 0; i < NumOutputs; i++) {
// TODO: expose memo in the public interface
// 0xF6 is invalid UTF8 as per spec
boost::array<unsigned char, ZC_MEMO_SIZE> memo = {{0xF6}};
NotePlaintext pt(out_notes[i], memo);
out_ciphertexts[i] = pt.encrypt(encryptor, outputs[i].addr.pk_enc);
}
out_ephemeralKey = encryptor.get_epk();
}
// Authenticate h_sig with each of the input
// spending keys, producing macs which protect
// against malleability.
for (size_t i = 0; i < NumInputs; i++) {
out_macs[i] = PRF_pk(inputs[i].key, i, h_sig);
}
std::vector<FieldT> primary_input;
std::vector<FieldT> aux_input;
{
protoboard<FieldT> pb;
{
joinsplit_gadget<FieldT, NumInputs, NumOutputs> g(pb);
g.generate_r1cs_constraints();
g.generate_r1cs_witness(
phi,
rt,
h_sig,
inputs,
out_notes,
vpub_old,
vpub_new
);
}
if (!pb.is_satisfied()) {
throw std::invalid_argument("Constraint system not satisfied by inputs");
}
primary_input = pb.primary_input();
aux_input = pb.auxiliary_input();
}
auto proof = r1cs_ppzksnark_prover<ppzksnark_ppT>(
*pk,
primary_input,
aux_input
);
std::stringstream ss;
ss << proof;
return ss.str();
}
};
template<size_t NumInputs, size_t NumOutputs>
JoinSplit<NumInputs, NumOutputs>* JoinSplit<NumInputs, NumOutputs>::Generate()
{
JoinSplitCircuit<NumInputs, NumOutputs>::initialize();
auto js = new JoinSplitCircuit<NumInputs, NumOutputs>();
js->generate();
return js;
}
template<size_t NumInputs, size_t NumOutputs>
JoinSplit<NumInputs, NumOutputs>* JoinSplit<NumInputs, NumOutputs>::Unopened()
{
JoinSplitCircuit<NumInputs, NumOutputs>::initialize();
return new JoinSplitCircuit<NumInputs, NumOutputs>();
}
template<size_t NumInputs, size_t NumOutputs>
uint256 JoinSplit<NumInputs, NumOutputs>::h_sig(
const uint256& randomSeed,
const boost::array<uint256, NumInputs>& nullifiers,
const uint256& pubKeyHash
) {
unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES]
= {'Z','c','a','s','h','C','o','m','p','u','t','e','h','S','i','g'};
std::vector<unsigned char> block(randomSeed.begin(), randomSeed.end());
for (size_t i = 0; i < NumInputs; i++) {
block.insert(block.end(), nullifiers[i].begin(), nullifiers[i].end());
}
block.insert(block.end(), pubKeyHash.begin(), pubKeyHash.end());
uint256 output;
if (crypto_generichash_blake2b_salt_personal(output.begin(), 32,
&block[0], block.size(),
NULL, 0, // No key.
NULL, // No salt.
personalization
) != 0)
{
throw std::logic_error("hash function failure");
}
return output;
}
Note JSOutput::note(const uint256& phi, const uint256& r, size_t i, const uint256& h_sig) const {
uint256 rho = PRF_rho(phi, i, h_sig);
return Note(addr.a_pk, value, rho, r);
}
JSOutput::JSOutput() : addr(uint256(), uint256()), value(0) {
SpendingKey a_sk(random_uint256());
addr = a_sk.address();
}
JSInput::JSInput() : witness(ZCIncrementalMerkleTree().witness()),
key(random_uint256()) {
note = Note(key.address().a_pk, 0, random_uint256(), random_uint256());
ZCIncrementalMerkleTree dummy_tree;
dummy_tree.append(note.cm());
witness = dummy_tree.witness();
}
template class JoinSplit<ZC_NUM_JS_INPUTS,
ZC_NUM_JS_OUTPUTS>;
}

98
src/zcash/JoinSplit.hpp Normal file
View File

@ -0,0 +1,98 @@
#ifndef _ZCJOINSPLIT_H_
#define _ZCJOINSPLIT_H_
#include "Zcash.h"
#include "Address.hpp"
#include "Note.hpp"
#include "IncrementalMerkleTree.hpp"
#include "NoteEncryption.hpp"
#include "uint256.h"
#include <boost/array.hpp>
namespace libzcash {
class JSInput {
public:
ZCIncrementalWitness witness;
Note note;
SpendingKey key;
JSInput();
JSInput(ZCIncrementalWitness witness,
Note note,
SpendingKey key) : witness(witness), note(note), key(key) { }
uint256 nullifier() const {
return note.nullifier(key);
}
};
class JSOutput {
public:
PaymentAddress addr;
uint64_t value;
JSOutput();
JSOutput(PaymentAddress addr, uint64_t value) : addr(addr), value(value) { }
Note note(const uint256& phi, const uint256& r, size_t i, const uint256& h_sig) const;
};
template<size_t NumInputs, size_t NumOutputs>
class JoinSplit {
public:
static JoinSplit<NumInputs, NumOutputs>* Generate();
static JoinSplit<NumInputs, NumOutputs>* Unopened();
static uint256 h_sig(const uint256& randomSeed,
const boost::array<uint256, NumInputs>& nullifiers,
const uint256& pubKeyHash
);
// TODO: #789
virtual void setProvingKeyPath(std::string) = 0;
virtual void loadProvingKey() = 0;
virtual void saveProvingKey(std::string path) = 0;
virtual void loadVerifyingKey(std::string path) = 0;
virtual void saveVerifyingKey(std::string path) = 0;
virtual std::string prove(
const boost::array<JSInput, NumInputs>& inputs,
const boost::array<JSOutput, NumOutputs>& outputs,
boost::array<Note, NumOutputs>& out_notes,
boost::array<ZCNoteEncryption::Ciphertext, NumOutputs>& out_ciphertexts,
uint256& out_ephemeralKey,
const uint256& pubKeyHash,
uint256& out_randomSeed,
boost::array<uint256, NumInputs>& out_hmacs,
boost::array<uint256, NumInputs>& out_nullifiers,
boost::array<uint256, NumOutputs>& out_commitments,
uint64_t vpub_old,
uint64_t vpub_new,
const uint256& rt
) = 0;
virtual bool verify(
const std::string& proof,
const uint256& pubKeyHash,
const uint256& randomSeed,
const boost::array<uint256, NumInputs>& hmacs,
const boost::array<uint256, NumInputs>& nullifiers,
const boost::array<uint256, NumOutputs>& commitments,
uint64_t vpub_old,
uint64_t vpub_new,
const uint256& rt
) = 0;
protected:
JoinSplit() {}
};
}
typedef libzcash::JoinSplit<ZC_NUM_JS_INPUTS,
ZC_NUM_JS_OUTPUTS> ZCJoinSplit;
#endif // _ZCJOINSPLIT_H_

92
src/zcash/Note.cpp Normal file
View File

@ -0,0 +1,92 @@
#include "Note.hpp"
#include "prf.h"
#include "crypto/sha256.h"
#include "version.h"
#include "streams.h"
#include "zcash/util.h"
namespace libzcash {
Note::Note() {
a_pk = random_uint256();
rho = random_uint256();
r = random_uint256();
value = 0;
}
uint256 Note::cm() const {
unsigned char discriminant = 0xb0;
CSHA256 hasher;
hasher.Write(&discriminant, 1);
hasher.Write(a_pk.begin(), 32);
auto value_vec = convertIntToVectorLE(value);
hasher.Write(&value_vec[0], value_vec.size());
hasher.Write(rho.begin(), 32);
hasher.Write(r.begin(), 32);
uint256 result;
hasher.Finalize(result.begin());
return result;
}
uint256 Note::nullifier(const SpendingKey& a_sk) const {
return PRF_nf(a_sk, rho);
}
NotePlaintext::NotePlaintext(
const Note& note,
boost::array<unsigned char, ZC_MEMO_SIZE> memo) : memo(memo)
{
value = note.value;
rho = note.rho;
r = note.r;
}
Note NotePlaintext::note(const PaymentAddress& addr) const
{
return Note(addr.a_pk, value, rho, r);
}
NotePlaintext NotePlaintext::decrypt(const ZCNoteDecryption& decryptor,
const ZCNoteDecryption::Ciphertext& ciphertext,
const uint256& ephemeralKey,
const uint256& h_sig,
unsigned char nonce
)
{
auto plaintext = decryptor.decrypt(ciphertext, ephemeralKey, h_sig, nonce);
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << plaintext;
NotePlaintext ret;
ss >> ret;
assert(ss.size() == 0);
return ret;
}
ZCNoteEncryption::Ciphertext NotePlaintext::encrypt(ZCNoteEncryption& encryptor,
const uint256& pk_enc
) const
{
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << (*this);
ZCNoteEncryption::Plaintext pt;
assert(pt.size() == ss.size());
memcpy(&pt[0], &ss[0], pt.size());
return encryptor.encrypt(pk_enc, pt);
}
}

71
src/zcash/Note.hpp Normal file
View File

@ -0,0 +1,71 @@
#ifndef _ZCNOTE_H_
#define _ZCNOTE_H_
#include "uint256.h"
#include "Zcash.h"
#include "Address.hpp"
#include "NoteEncryption.hpp"
namespace libzcash {
class Note {
public:
uint256 a_pk;
uint64_t value;
uint256 rho;
uint256 r;
Note(uint256 a_pk, uint64_t value, uint256 rho, uint256 r)
: a_pk(a_pk), value(value), rho(rho), r(r) {}
Note();
uint256 cm() const;
uint256 nullifier(const SpendingKey& a_sk) const;
};
class NotePlaintext {
public:
uint64_t value;
uint256 rho;
uint256 r;
boost::array<unsigned char, ZC_MEMO_SIZE> memo;
NotePlaintext() {}
NotePlaintext(const Note& note, boost::array<unsigned char, ZC_MEMO_SIZE> memo);
Note note(const PaymentAddress& addr) const;
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
unsigned char leadingByte = 0x00;
READWRITE(leadingByte);
if (leadingByte != 0x00) {
throw std::ios_base::failure("lead byte of NotePlaintext is not recognized");
}
READWRITE(value);
READWRITE(rho);
READWRITE(r);
READWRITE(memo);
}
static NotePlaintext decrypt(const ZCNoteDecryption& decryptor,
const ZCNoteDecryption::Ciphertext& ciphertext,
const uint256& ephemeralKey,
const uint256& h_sig,
unsigned char nonce
);
ZCNoteEncryption::Ciphertext encrypt(ZCNoteEncryption& encryptor,
const uint256& pk_enc
) const;
};
}
#endif // _ZCNOTE_H_

View File

@ -165,7 +165,7 @@ uint256 random_uint256()
return ret;
}
template class NoteEncryption<ZC_V_SIZE + ZC_RHO_SIZE + ZC_R_SIZE + ZC_MEMO_SIZE>;
template class NoteDecryption<ZC_V_SIZE + ZC_RHO_SIZE + ZC_R_SIZE + ZC_MEMO_SIZE>;
template class NoteEncryption<ZC_NOTEPLAINTEXT_LEADING + ZC_V_SIZE + ZC_RHO_SIZE + ZC_R_SIZE + ZC_MEMO_SIZE>;
template class NoteDecryption<ZC_NOTEPLAINTEXT_LEADING + ZC_V_SIZE + ZC_RHO_SIZE + ZC_R_SIZE + ZC_MEMO_SIZE>;
}

View File

@ -9,7 +9,7 @@ https://github.com/zcash/zips/blob/master/protocol/protocol.pdf
#include <boost/array.hpp>
#include "uint256.h"
#include "zerocash/Zerocash.h"
#include "zcash/Zcash.h"
namespace libzcash {
@ -73,7 +73,7 @@ uint256 random_uint256();
}
typedef libzcash::NoteEncryption<ZC_V_SIZE + ZC_RHO_SIZE + ZC_R_SIZE + ZC_MEMO_SIZE> ZCNoteEncryption;
typedef libzcash::NoteDecryption<ZC_V_SIZE + ZC_RHO_SIZE + ZC_R_SIZE + ZC_MEMO_SIZE> ZCNoteDecryption;
typedef libzcash::NoteEncryption<ZC_NOTEPLAINTEXT_LEADING + ZC_V_SIZE + ZC_RHO_SIZE + ZC_R_SIZE + ZC_MEMO_SIZE> ZCNoteEncryption;
typedef libzcash::NoteDecryption<ZC_NOTEPLAINTEXT_LEADING + ZC_V_SIZE + ZC_RHO_SIZE + ZC_R_SIZE + ZC_MEMO_SIZE> ZCNoteDecryption;
#endif /* ZC_NOTE_ENCRYPTION_H_ */

15
src/zcash/Zcash.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef _ZCCONSTANTS_H_
#define _ZCCONSTANTS_H_
#define ZC_NUM_JS_INPUTS 2
#define ZC_NUM_JS_OUTPUTS 2
#define INCREMENTAL_MERKLE_TREE_DEPTH 20
#define INCREMENTAL_MERKLE_TREE_DEPTH_TESTING 4
#define ZC_NOTEPLAINTEXT_LEADING 1
#define ZC_V_SIZE 8
#define ZC_RHO_SIZE 32
#define ZC_R_SIZE 32
#define ZC_MEMO_SIZE 128
#endif // _ZCCONSTANTS_H_

View File

@ -0,0 +1,104 @@
template<typename FieldT>
class note_commitment_gadget : gadget<FieldT> {
private:
std::shared_ptr<block_variable<FieldT>> block1;
std::shared_ptr<block_variable<FieldT>> block2;
std::shared_ptr<sha256_compression_function_gadget<FieldT>> hasher1;
std::shared_ptr<digest_variable<FieldT>> intermediate_hash;
std::shared_ptr<sha256_compression_function_gadget<FieldT>> hasher2;
public:
note_commitment_gadget(
protoboard<FieldT> &pb,
pb_variable<FieldT>& ZERO,
pb_variable_array<FieldT>& a_pk,
pb_variable_array<FieldT>& v,
pb_variable_array<FieldT>& rho,
pb_variable_array<FieldT>& r,
std::shared_ptr<digest_variable<FieldT>> result
) : gadget<FieldT>(pb) {
pb_variable_array<FieldT> leading_byte =
from_bits({1, 0, 1, 1, 0, 0, 0, 0}, ZERO);
pb_variable_array<FieldT> first_of_rho(rho.begin(), rho.begin()+184);
pb_variable_array<FieldT> last_of_rho(rho.begin()+184, rho.end());
intermediate_hash.reset(new digest_variable<FieldT>(pb, 256, ""));
// final padding
pb_variable_array<FieldT> length_padding =
from_bits({
// padding
1,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
// length of message (840 bits)
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,1,1,
0,1,0,0,1,0,0,0
}, ZERO);
block1.reset(new block_variable<FieldT>(pb, {
leading_byte,
a_pk,
v,
first_of_rho
}, ""));
block2.reset(new block_variable<FieldT>(pb, {
last_of_rho,
r,
length_padding
}, ""));
pb_linear_combination_array<FieldT> IV = SHA256_default_IV(pb);
hasher1.reset(new sha256_compression_function_gadget<FieldT>(
pb,
IV,
block1->bits,
*intermediate_hash,
""));
pb_linear_combination_array<FieldT> IV2(intermediate_hash->bits);
hasher2.reset(new sha256_compression_function_gadget<FieldT>(
pb,
IV2,
block2->bits,
*result,
""));
}
void generate_r1cs_constraints() {
// TODO: This may not be necessary if SHA256 constrains
// its output digests to be boolean anyway.
intermediate_hash->generate_r1cs_constraints();
hasher1->generate_r1cs_constraints();
hasher2->generate_r1cs_constraints();
}
void generate_r1cs_witness() {
hasher1->generate_r1cs_witness();
hasher2->generate_r1cs_witness();
}
};

View File

@ -0,0 +1,349 @@
#include "zcash/circuit/utils.tcc"
#include "zcash/circuit/prfs.tcc"
#include "zcash/circuit/commitment.tcc"
#include "zcash/circuit/merkle.tcc"
#include "zcash/circuit/note.tcc"
template<typename FieldT, size_t NumInputs, size_t NumOutputs>
class joinsplit_gadget : gadget<FieldT> {
private:
// Verifier inputs
pb_variable_array<FieldT> zk_packed_inputs;
pb_variable_array<FieldT> zk_unpacked_inputs;
std::shared_ptr<multipacking_gadget<FieldT>> unpacker;
std::shared_ptr<digest_variable<FieldT>> zk_merkle_root;
std::shared_ptr<digest_variable<FieldT>> zk_h_sig;
boost::array<std::shared_ptr<digest_variable<FieldT>>, NumInputs> zk_input_nullifiers;
boost::array<std::shared_ptr<digest_variable<FieldT>>, NumInputs> zk_input_macs;
boost::array<std::shared_ptr<digest_variable<FieldT>>, NumOutputs> zk_output_commitments;
pb_variable_array<FieldT> zk_vpub_old;
pb_variable_array<FieldT> zk_vpub_new;
// Aux inputs
pb_variable<FieldT> ZERO;
std::shared_ptr<digest_variable<FieldT>> zk_phi;
pb_variable_array<FieldT> zk_total_uint64;
// Input note gadgets
boost::array<std::shared_ptr<input_note_gadget<FieldT>>, NumInputs> zk_input_notes;
boost::array<std::shared_ptr<PRF_pk_gadget<FieldT>>, NumInputs> zk_mac_authentication;
// Output note gadgets
boost::array<std::shared_ptr<output_note_gadget<FieldT>>, NumOutputs> zk_output_notes;
public:
// PRF_pk only has a 1-bit domain separation "nonce"
// for different macs.
BOOST_STATIC_ASSERT(NumInputs <= 2);
// PRF_rho only has a 1-bit domain separation "nonce"
// for different output `rho`.
BOOST_STATIC_ASSERT(NumOutputs <= 2);
joinsplit_gadget(protoboard<FieldT> &pb) : gadget<FieldT>(pb) {
// Verification
{
// The verification inputs are all bit-strings of various
// lengths (256-bit digests and 64-bit integers) and so we
// pack them into as few field elements as possible. (The
// more verification inputs you have, the more expensive
// verification is.)
zk_packed_inputs.allocate(pb, verifying_field_element_size());
pb.set_input_sizes(verifying_field_element_size());
alloc_uint256(zk_unpacked_inputs, zk_merkle_root);
alloc_uint256(zk_unpacked_inputs, zk_h_sig);
for (size_t i = 0; i < NumInputs; i++) {
alloc_uint256(zk_unpacked_inputs, zk_input_nullifiers[i]);
alloc_uint256(zk_unpacked_inputs, zk_input_macs[i]);
}
for (size_t i = 0; i < NumOutputs; i++) {
alloc_uint256(zk_unpacked_inputs, zk_output_commitments[i]);
}
alloc_uint64(zk_unpacked_inputs, zk_vpub_old);
alloc_uint64(zk_unpacked_inputs, zk_vpub_new);
assert(zk_unpacked_inputs.size() == verifying_input_bit_size());
// This gadget will ensure that all of the inputs we provide are
// boolean constrained.
unpacker.reset(new multipacking_gadget<FieldT>(
pb,
zk_unpacked_inputs,
zk_packed_inputs,
FieldT::capacity(),
"unpacker"
));
}
// We need a constant "zero" variable in some contexts. In theory
// it should never be necessary, but libsnark does not synthesize
// optimal circuits.
//
// The first variable of our constraint system is constrained
// to be one automatically for us, and is known as `ONE`.
ZERO.allocate(pb);
zk_phi.reset(new digest_variable<FieldT>(pb, 252, ""));
zk_total_uint64.allocate(pb, 64);
for (size_t i = 0; i < NumInputs; i++) {
// Input note gadget for commitments, macs, nullifiers,
// and spend authority.
zk_input_notes[i].reset(new input_note_gadget<FieldT>(
pb,
ZERO,
zk_input_nullifiers[i],
*zk_merkle_root
));
// The input keys authenticate h_sig to prevent
// malleability.
zk_mac_authentication[i].reset(new PRF_pk_gadget<FieldT>(
pb,
ZERO,
zk_input_notes[i]->a_sk->bits,
zk_h_sig->bits,
i ? true : false,
zk_input_macs[i]
));
}
for (size_t i = 0; i < NumOutputs; i++) {
zk_output_notes[i].reset(new output_note_gadget<FieldT>(
pb,
ZERO,
zk_phi->bits,
zk_h_sig->bits,
i ? true : false,
zk_output_commitments[i]
));
}
}
void generate_r1cs_constraints() {
// The true passed here ensures all the inputs
// are boolean constrained.
unpacker->generate_r1cs_constraints(true);
// Constrain `ZERO`
generate_r1cs_equals_const_constraint<FieldT>(this->pb, ZERO, FieldT::zero(), "ZERO");
// Constrain bitness of phi
zk_phi->generate_r1cs_constraints();
for (size_t i = 0; i < NumInputs; i++) {
// Constrain the JoinSplit input constraints.
zk_input_notes[i]->generate_r1cs_constraints();
// Authenticate h_sig with a_sk
zk_mac_authentication[i]->generate_r1cs_constraints();
}
for (size_t i = 0; i < NumOutputs; i++) {
// Constrain the JoinSplit output constraints.
zk_output_notes[i]->generate_r1cs_constraints();
}
// Value balance
{
linear_combination<FieldT> left_side = packed_addition(zk_vpub_old);
for (size_t i = 0; i < NumInputs; i++) {
left_side = left_side + packed_addition(zk_input_notes[i]->value);
}
linear_combination<FieldT> right_side = packed_addition(zk_vpub_new);
for (size_t i = 0; i < NumOutputs; i++) {
right_side = right_side + packed_addition(zk_output_notes[i]->value);
}
// Ensure that both sides are equal
this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(
1,
left_side,
right_side
));
// #854: Ensure that left_side is a 64-bit integer.
for (size_t i = 0; i < 64; i++) {
generate_boolean_r1cs_constraint<FieldT>(
this->pb,
zk_total_uint64[i],
""
);
}
this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(
1,
left_side,
packed_addition(zk_total_uint64)
));
}
}
void generate_r1cs_witness(
const uint256& phi,
const uint256& rt,
const uint256& h_sig,
const boost::array<JSInput, NumInputs>& inputs,
const boost::array<Note, NumOutputs>& outputs,
uint64_t vpub_old,
uint64_t vpub_new
) {
// Witness `zero`
this->pb.val(ZERO) = FieldT::zero();
// Witness rt. This is not a sanity check.
//
// This ensures the read gadget constrains
// the intended root in the event that
// both inputs are zero-valued.
zk_merkle_root->bits.fill_with_bits(
this->pb,
uint256_to_bool_vector(rt)
);
// Witness public balance values
zk_vpub_old.fill_with_bits(
this->pb,
uint64_to_bool_vector(vpub_old)
);
zk_vpub_new.fill_with_bits(
this->pb,
uint64_to_bool_vector(vpub_new)
);
{
// Witness total_uint64 bits
uint64_t left_side_acc = vpub_old;
for (size_t i = 0; i < NumInputs; i++) {
left_side_acc += inputs[i].note.value;
}
zk_total_uint64.fill_with_bits(
this->pb,
uint64_to_bool_vector(left_side_acc)
);
}
// Witness phi
zk_phi->bits.fill_with_bits(
this->pb,
trailing252(uint256_to_bool_vector(phi))
);
// Witness h_sig
zk_h_sig->bits.fill_with_bits(
this->pb,
uint256_to_bool_vector(h_sig)
);
for (size_t i = 0; i < NumInputs; i++) {
// Witness the input information.
auto merkle_path = inputs[i].witness.path();
zk_input_notes[i]->generate_r1cs_witness(
merkle_path,
inputs[i].key,
inputs[i].note
);
// Witness macs
zk_mac_authentication[i]->generate_r1cs_witness();
}
for (size_t i = 0; i < NumOutputs; i++) {
// Witness the output information.
zk_output_notes[i]->generate_r1cs_witness(outputs[i]);
}
// [SANITY CHECK] Ensure that the intended root
// was witnessed by the inputs, even if the read
// gadget overwrote it. This allows the prover to
// fail instead of the verifier, in the event that
// the roots of the inputs do not match the
// treestate provided to the proving API.
zk_merkle_root->bits.fill_with_bits(
this->pb,
uint256_to_bool_vector(rt)
);
// This happens last, because only by now are all the
// verifier inputs resolved.
unpacker->generate_r1cs_witness_from_bits();
}
static r1cs_primary_input<FieldT> witness_map(
const uint256& rt,
const uint256& h_sig,
const boost::array<uint256, NumInputs>& macs,
const boost::array<uint256, NumInputs>& nullifiers,
const boost::array<uint256, NumOutputs>& commitments,
uint64_t vpub_old,
uint64_t vpub_new
) {
std::vector<bool> verify_inputs;
insert_uint256(verify_inputs, rt);
insert_uint256(verify_inputs, h_sig);
for (size_t i = 0; i < NumInputs; i++) {
insert_uint256(verify_inputs, nullifiers[i]);
insert_uint256(verify_inputs, macs[i]);
}
for (size_t i = 0; i < NumOutputs; i++) {
insert_uint256(verify_inputs, commitments[i]);
}
insert_uint64(verify_inputs, vpub_old);
insert_uint64(verify_inputs, vpub_new);
assert(verify_inputs.size() == verifying_input_bit_size());
auto verify_field_elements = pack_bit_vector_into_field_element_vector<FieldT>(verify_inputs);
assert(verify_field_elements.size() == verifying_field_element_size());
return verify_field_elements;
}
static size_t verifying_input_bit_size() {
size_t acc = 0;
acc += 256; // the merkle root (anchor)
acc += 256; // h_sig
for (size_t i = 0; i < NumInputs; i++) {
acc += 256; // nullifier
acc += 256; // mac
}
for (size_t i = 0; i < NumOutputs; i++) {
acc += 256; // new commitment
}
acc += 64; // vpub_old
acc += 64; // vpub_new
return acc;
}
static size_t verifying_field_element_size() {
return div_ceil(verifying_input_bit_size(), FieldT::capacity());
}
void alloc_uint256(
pb_variable_array<FieldT>& packed_into,
std::shared_ptr<digest_variable<FieldT>>& var
) {
var.reset(new digest_variable<FieldT>(this->pb, 256, ""));
packed_into.insert(packed_into.end(), var->bits.begin(), var->bits.end());
}
void alloc_uint64(
pb_variable_array<FieldT>& packed_into,
pb_variable_array<FieldT>& integer
) {
integer.allocate(this->pb, 64, "");
packed_into.insert(packed_into.end(), integer.begin(), integer.end());
}
};

View File

@ -0,0 +1,60 @@
template<typename FieldT>
class merkle_tree_gadget : gadget<FieldT> {
private:
typedef sha256_two_to_one_hash_gadget<FieldT> sha256_gadget;
pb_variable_array<FieldT> positions;
std::shared_ptr<merkle_authentication_path_variable<FieldT, sha256_gadget>> authvars;
std::shared_ptr<merkle_tree_check_read_gadget<FieldT, sha256_gadget>> auth;
public:
merkle_tree_gadget(
protoboard<FieldT>& pb,
digest_variable<FieldT> leaf,
digest_variable<FieldT> root,
pb_variable<FieldT>& enforce
) : gadget<FieldT>(pb) {
positions.allocate(pb, INCREMENTAL_MERKLE_TREE_DEPTH);
authvars.reset(new merkle_authentication_path_variable<FieldT, sha256_gadget>(
pb, INCREMENTAL_MERKLE_TREE_DEPTH, "auth"
));
auth.reset(new merkle_tree_check_read_gadget<FieldT, sha256_gadget>(
pb,
INCREMENTAL_MERKLE_TREE_DEPTH,
positions,
leaf,
root,
*authvars,
enforce,
""
));
}
void generate_r1cs_constraints() {
for (size_t i = 0; i < INCREMENTAL_MERKLE_TREE_DEPTH; i++) {
// TODO: This might not be necessary, and doesn't
// appear to be done in libsnark's tests, but there
// is no documentation, so let's do it anyway to
// be safe.
generate_boolean_r1cs_constraint<FieldT>(
this->pb,
positions[i],
"boolean_positions"
);
}
authvars->generate_r1cs_constraints();
auth->generate_r1cs_constraints();
}
void generate_r1cs_witness(const MerklePath& path) {
// TODO: Change libsnark so that it doesn't require this goofy
// number thing in its API.
size_t path_index = libzerocash::convertVectorToInt(path.index);
positions.fill_with_bits_of_ulong(this->pb, path_index);
authvars->generate_r1cs_witness(path_index, path.authentication_path);
auth->generate_r1cs_witness();
}
};

253
src/zcash/circuit/note.tcc Normal file
View File

@ -0,0 +1,253 @@
template<typename FieldT>
class note_gadget : public gadget<FieldT> {
public:
pb_variable_array<FieldT> value;
std::shared_ptr<digest_variable<FieldT>> r;
note_gadget(protoboard<FieldT> &pb) : gadget<FieldT>(pb) {
value.allocate(pb, 64);
r.reset(new digest_variable<FieldT>(pb, 256, ""));
}
void generate_r1cs_constraints() {
for (size_t i = 0; i < 64; i++) {
generate_boolean_r1cs_constraint<FieldT>(
this->pb,
value[i],
"boolean_value"
);
}
r->generate_r1cs_constraints();
}
void generate_r1cs_witness(const Note& note) {
r->bits.fill_with_bits(this->pb, uint256_to_bool_vector(note.r));
value.fill_with_bits(this->pb, uint64_to_bool_vector(note.value));
}
};
template<typename FieldT>
class input_note_gadget : public note_gadget<FieldT> {
private:
std::shared_ptr<digest_variable<FieldT>> a_pk;
std::shared_ptr<digest_variable<FieldT>> rho;
std::shared_ptr<digest_variable<FieldT>> commitment;
std::shared_ptr<note_commitment_gadget<FieldT>> commit_to_inputs;
pb_variable<FieldT> value_enforce;
std::shared_ptr<merkle_tree_gadget<FieldT>> witness_input;
std::shared_ptr<PRF_addr_a_pk_gadget<FieldT>> spend_authority;
std::shared_ptr<PRF_nf_gadget<FieldT>> expose_nullifiers;
public:
std::shared_ptr<digest_variable<FieldT>> a_sk;
input_note_gadget(
protoboard<FieldT>& pb,
pb_variable<FieldT>& ZERO,
std::shared_ptr<digest_variable<FieldT>> nullifier,
digest_variable<FieldT> rt
) : note_gadget<FieldT>(pb) {
a_sk.reset(new digest_variable<FieldT>(pb, 252, ""));
a_pk.reset(new digest_variable<FieldT>(pb, 256, ""));
rho.reset(new digest_variable<FieldT>(pb, 256, ""));
commitment.reset(new digest_variable<FieldT>(pb, 256, ""));
spend_authority.reset(new PRF_addr_a_pk_gadget<FieldT>(
pb,
ZERO,
a_sk->bits,
a_pk
));
expose_nullifiers.reset(new PRF_nf_gadget<FieldT>(
pb,
ZERO,
a_sk->bits,
rho->bits,
nullifier
));
commit_to_inputs.reset(new note_commitment_gadget<FieldT>(
pb,
ZERO,
a_pk->bits,
this->value,
rho->bits,
this->r->bits,
commitment
));
value_enforce.allocate(pb);
witness_input.reset(new merkle_tree_gadget<FieldT>(
pb,
*commitment,
rt,
value_enforce
));
}
void generate_r1cs_constraints() {
note_gadget<FieldT>::generate_r1cs_constraints();
a_sk->generate_r1cs_constraints();
rho->generate_r1cs_constraints();
// TODO: These constraints may not be necessary if SHA256
// already boolean constrains its outputs.
a_pk->generate_r1cs_constraints();
commitment->generate_r1cs_constraints();
spend_authority->generate_r1cs_constraints();
expose_nullifiers->generate_r1cs_constraints();
commit_to_inputs->generate_r1cs_constraints();
// value * (1 - enforce) = 0
// Given `enforce` is boolean constrained:
// If `value` is zero, `enforce` _can_ be zero.
// If `value` is nonzero, `enforce` _must_ be one.
generate_boolean_r1cs_constraint<FieldT>(this->pb, value_enforce,"");
this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(
packed_addition(this->value),
(1 - value_enforce),
0
), "");
witness_input->generate_r1cs_constraints();
}
void generate_r1cs_witness(
const MerklePath& path,
const SpendingKey& key,
const Note& note
) {
note_gadget<FieldT>::generate_r1cs_witness(note);
// Witness a_sk for the input
a_sk->bits.fill_with_bits(
this->pb,
trailing252(uint256_to_bool_vector(key))
);
// Witness a_pk for a_sk with PRF_addr
spend_authority->generate_r1cs_witness();
// [SANITY CHECK] Witness a_pk with note information
a_pk->bits.fill_with_bits(
this->pb,
uint256_to_bool_vector(note.a_pk)
);
// Witness rho for the input note
rho->bits.fill_with_bits(
this->pb,
uint256_to_bool_vector(note.rho)
);
// Witness the nullifier for the input note
expose_nullifiers->generate_r1cs_witness();
// Witness the commitment of the input note
commit_to_inputs->generate_r1cs_witness();
// [SANITY CHECK] Ensure the commitment is
// valid.
commitment->bits.fill_with_bits(
this->pb,
uint256_to_bool_vector(note.cm())
);
// Set enforce flag for nonzero input value
this->pb.val(value_enforce) = (note.value != 0) ? FieldT::one() : FieldT::zero();
// Witness merkle tree authentication path
witness_input->generate_r1cs_witness(path);
}
};
template<typename FieldT>
class output_note_gadget : public note_gadget<FieldT> {
private:
std::shared_ptr<digest_variable<FieldT>> rho;
std::shared_ptr<digest_variable<FieldT>> a_pk;
std::shared_ptr<PRF_rho_gadget<FieldT>> prevent_faerie_gold;
std::shared_ptr<note_commitment_gadget<FieldT>> commit_to_outputs;
public:
output_note_gadget(
protoboard<FieldT>& pb,
pb_variable<FieldT>& ZERO,
pb_variable_array<FieldT>& phi,
pb_variable_array<FieldT>& h_sig,
bool nonce,
std::shared_ptr<digest_variable<FieldT>> commitment
) : note_gadget<FieldT>(pb) {
rho.reset(new digest_variable<FieldT>(pb, 256, ""));
a_pk.reset(new digest_variable<FieldT>(pb, 256, ""));
// Do not allow the caller to choose the same "rho"
// for any two valid notes in a given view of the
// blockchain. See protocol specification for more
// details.
prevent_faerie_gold.reset(new PRF_rho_gadget<FieldT>(
pb,
ZERO,
phi,
h_sig,
nonce,
rho
));
// Commit to the output notes publicly without
// disclosing them.
commit_to_outputs.reset(new note_commitment_gadget<FieldT>(
pb,
ZERO,
a_pk->bits,
this->value,
rho->bits,
this->r->bits,
commitment
));
}
void generate_r1cs_constraints() {
note_gadget<FieldT>::generate_r1cs_constraints();
a_pk->generate_r1cs_constraints();
// TODO: This constraint may not be necessary if SHA256
// already boolean constrains its outputs.
rho->generate_r1cs_constraints();
prevent_faerie_gold->generate_r1cs_constraints();
commit_to_outputs->generate_r1cs_constraints();
}
void generate_r1cs_witness(const Note& note) {
note_gadget<FieldT>::generate_r1cs_witness(note);
prevent_faerie_gold->generate_r1cs_witness();
// [SANITY CHECK] Witness rho ourselves with the
// note information.
rho->bits.fill_with_bits(
this->pb,
uint256_to_bool_vector(note.rho)
);
a_pk->bits.fill_with_bits(
this->pb,
uint256_to_bool_vector(note.a_pk)
);
commit_to_outputs->generate_r1cs_witness();
}
};

107
src/zcash/circuit/prfs.tcc Normal file
View File

@ -0,0 +1,107 @@
template<typename FieldT>
class PRF_gadget : gadget<FieldT> {
private:
std::shared_ptr<block_variable<FieldT>> block;
std::shared_ptr<sha256_compression_function_gadget<FieldT>> hasher;
std::shared_ptr<digest_variable<FieldT>> result;
public:
PRF_gadget(
protoboard<FieldT>& pb,
pb_variable<FieldT>& ZERO,
bool a,
bool b,
bool c,
bool d,
pb_variable_array<FieldT> x,
boost::optional<pb_variable_array<FieldT>> y,
std::shared_ptr<digest_variable<FieldT>> result
) : gadget<FieldT>(pb), result(result) {
pb_linear_combination_array<FieldT> IV = SHA256_default_IV(pb);
pb_variable_array<FieldT> discriminants;
discriminants.emplace_back(a ? ONE : ZERO);
discriminants.emplace_back(b ? ONE : ZERO);
discriminants.emplace_back(c ? ONE : ZERO);
discriminants.emplace_back(d ? ONE : ZERO);
if (!y) {
// Create y and pad it with zeroes.
y = pb_variable_array<FieldT>();
while (y->size() < 256) {
y->emplace_back(ZERO);
}
}
block.reset(new block_variable<FieldT>(pb, {
discriminants,
x,
*y
}, "PRF_block"));
hasher.reset(new sha256_compression_function_gadget<FieldT>(
pb,
IV,
block->bits,
*result,
"PRF_hasher"));
}
void generate_r1cs_constraints() {
hasher->generate_r1cs_constraints();
}
void generate_r1cs_witness() {
hasher->generate_r1cs_witness();
}
};
template<typename FieldT>
class PRF_addr_a_pk_gadget : public PRF_gadget<FieldT> {
public:
PRF_addr_a_pk_gadget(
protoboard<FieldT>& pb,
pb_variable<FieldT>& ZERO,
pb_variable_array<FieldT>& a_sk,
std::shared_ptr<digest_variable<FieldT>> result
) : PRF_gadget<FieldT>(pb, ZERO, 1, 1, 0, 0, a_sk, boost::none, result) {}
};
template<typename FieldT>
class PRF_nf_gadget : public PRF_gadget<FieldT> {
public:
PRF_nf_gadget(
protoboard<FieldT>& pb,
pb_variable<FieldT>& ZERO,
pb_variable_array<FieldT>& a_sk,
pb_variable_array<FieldT>& rho,
std::shared_ptr<digest_variable<FieldT>> result
) : PRF_gadget<FieldT>(pb, ZERO, 1, 1, 1, 0, a_sk, rho, result) {}
};
template<typename FieldT>
class PRF_pk_gadget : public PRF_gadget<FieldT> {
public:
PRF_pk_gadget(
protoboard<FieldT>& pb,
pb_variable<FieldT>& ZERO,
pb_variable_array<FieldT>& a_sk,
pb_variable_array<FieldT>& h_sig,
bool nonce,
std::shared_ptr<digest_variable<FieldT>> result
) : PRF_gadget<FieldT>(pb, ZERO, 0, nonce, 0, 0, a_sk, h_sig, result) {}
};
template<typename FieldT>
class PRF_rho_gadget : public PRF_gadget<FieldT> {
public:
PRF_rho_gadget(
protoboard<FieldT>& pb,
pb_variable<FieldT>& ZERO,
pb_variable_array<FieldT>& phi,
pb_variable_array<FieldT>& h_sig,
bool nonce,
std::shared_ptr<digest_variable<FieldT>> result
) : PRF_gadget<FieldT>(pb, ZERO, 0, nonce, 1, 0, phi, h_sig, result) {}
};

View File

@ -0,0 +1,71 @@
template<typename FieldT>
pb_variable_array<FieldT> from_bits(std::vector<bool> bits, pb_variable<FieldT>& ZERO) {
pb_variable_array<FieldT> acc;
BOOST_FOREACH(bool bit, bits) {
acc.emplace_back(bit ? ONE : ZERO);
}
return acc;
}
std::vector<bool> trailing252(std::vector<bool> input) {
if (input.size() != 256) {
throw std::length_error("trailing252 input invalid length");
}
return std::vector<bool>(input.begin() + 4, input.end());
}
std::vector<bool> uint256_to_bool_vector(uint256 input) {
std::vector<unsigned char> input_v(input.begin(), input.end());
std::vector<bool> output_bv(256, 0);
libzerocash::convertBytesVectorToVector(
input_v,
output_bv
);
return output_bv;
}
std::vector<bool> uint64_to_bool_vector(uint64_t input) {
auto num_bv = convertIntToVectorLE(input);
std::vector<bool> num_v(64, 0);
libzerocash::convertBytesVectorToVector(num_bv, num_v);
return num_v;
}
void insert_uint256(std::vector<bool>& into, uint256 from) {
std::vector<bool> blob = uint256_to_bool_vector(from);
into.insert(into.end(), blob.begin(), blob.end());
}
void insert_uint64(std::vector<bool>& into, uint64_t from) {
std::vector<bool> num = uint64_to_bool_vector(from);
into.insert(into.end(), num.begin(), num.end());
}
template<typename T>
T swap_endianness_u64(T v) {
if (v.size() != 64) {
throw std::length_error("invalid bit length for 64-bit unsigned integer");
}
for (size_t i = 0; i < 4; i++) {
for (size_t j = 0; j < 8; j++) {
std::swap(v[i*8 + j], v[((7-i)*8)+j]);
}
}
return v;
}
template<typename FieldT>
linear_combination<FieldT> packed_addition(pb_variable_array<FieldT> input) {
auto input_swapped = swap_endianness_u64(input);
return pb_packing_sum<FieldT>(pb_variable_array<FieldT>(
input_swapped.rbegin(), input_swapped.rend()
));
}

12
src/zcash/util.cpp Normal file
View File

@ -0,0 +1,12 @@
#include "zcash/util.h"
#include <algorithm>
std::vector<unsigned char> convertIntToVectorLE(const uint64_t val_int) {
std::vector<unsigned char> bytes;
for(size_t i = 0; i < 8; i++) {
bytes.push_back(val_int >> (i * 8));
}
return bytes;
}

9
src/zcash/util.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef __ZCASH_UTIL_H
#define __ZCASH_UTIL_H
#include <vector>
#include <cstdint>
std::vector<unsigned char> convertIntToVectorLE(const uint64_t val_int);
#endif // __ZCASH_UTIL_H

View File

@ -1,8 +1,5 @@
#include "zerocash/IncrementalMerkleTree.h"
#include <unistd.h>
#include <boost/filesystem.hpp>
#include "zerocash/ZerocashParams.h"
#include "coins.h"
#include "util.h"
#include "init.h"
@ -15,6 +12,11 @@
#include "zcbenchmarks.h"
#include "zcash/Zcash.h"
#include "zcash/IncrementalMerkleTree.hpp"
using namespace libzcash;
struct timeval tv_start;
void timer_start()
@ -42,64 +44,52 @@ double benchmark_sleep()
double benchmark_parameter_loading()
{
// FIXME: this is duplicated with the actual loading code
boost::filesystem::path pk_path = ZC_GetParamsDir() / "zc-testnet-public-alpha-proving.key";
boost::filesystem::path vk_path = ZC_GetParamsDir() / "zc-testnet-public-alpha-verification.key";
boost::filesystem::path pk_path = ZC_GetParamsDir() / "z3-proving.key";
boost::filesystem::path vk_path = ZC_GetParamsDir() / "z3-verification.key";
timer_start();
auto vk_loaded = libzerocash::ZerocashParams::LoadVerificationKeyFromFile(
vk_path.string(),
INCREMENTAL_MERKLE_TREE_DEPTH
);
auto pk_loaded = libzerocash::ZerocashParams::LoadProvingKeyFromFile(
pk_path.string(),
INCREMENTAL_MERKLE_TREE_DEPTH
);
libzerocash::ZerocashParams zerocashParams = libzerocash::ZerocashParams(
INCREMENTAL_MERKLE_TREE_DEPTH,
&pk_loaded,
&vk_loaded
);
return timer_stop();
auto newParams = ZCJoinSplit::Unopened();
newParams->loadVerifyingKey(vk_path.string());
newParams->setProvingKeyPath(pk_path.string());
newParams->loadProvingKey();
double ret = timer_stop();
delete newParams;
return ret;
}
double benchmark_create_joinsplit()
{
CScript scriptPubKey;
std::vector<PourInput> vpourin;
std::vector<PourOutput> vpourout;
while (vpourin.size() < NUM_POUR_INPUTS) {
vpourin.push_back(PourInput(INCREMENTAL_MERKLE_TREE_DEPTH));
}
while (vpourout.size() < NUM_POUR_OUTPUTS) {
vpourout.push_back(PourOutput(0));
}
// TODO: #808
uint256 pubKeyHash;
/* Get the anchor of an empty commitment tree. */
IncrementalMerkleTree blank_tree(INCREMENTAL_MERKLE_TREE_DEPTH);
std::vector<unsigned char> newrt_v(32);
blank_tree.getRootValue(newrt_v);
uint256 anchor = uint256(newrt_v);
uint256 anchor = ZCIncrementalMerkleTree().root();
timer_start();
CPourTx pourtx(*pzerocashParams,
scriptPubKey,
CPourTx pourtx(*pzcashParams,
pubKeyHash,
anchor,
{vpourin[0], vpourin[1]},
{vpourout[0], vpourout[1]},
{JSInput(), JSInput()},
{JSOutput(), JSOutput()},
0,
0);
double ret = timer_stop();
assert(pourtx.Verify(*pzerocashParams));
assert(pourtx.Verify(*pzcashParams, pubKeyHash));
return ret;
}
double benchmark_verify_joinsplit(const CPourTx &joinsplit)
{
timer_start();
joinsplit.Verify(*pzerocashParams);
// TODO: #808
uint256 pubKeyHash;
joinsplit.Verify(*pzcashParams, pubKeyHash);
return timer_stop();
}

View File

@ -1,132 +0,0 @@
/** @file
*****************************************************************************
Implementation of interfaces for the classes Address and PublicAddress.
See Address.h .
*****************************************************************************
* @author This file is part of libzerocash, developed by the Zerocash
* project and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#include "zcash/NoteEncryption.hpp"
#include "Zerocash.h"
#include "Address.h"
namespace libzerocash {
PrivateAddress::PrivateAddress(const uint256 &a_sk, const uint256 &sk_enc) {
this->a_sk = a_sk;
this->sk_enc = sk_enc;
}
PrivateAddress::PrivateAddress() {
}
bool PrivateAddress::operator==(const PrivateAddress& rhs) const {
return ((this->a_sk == rhs.a_sk) && (this->sk_enc == rhs.sk_enc));
}
bool PrivateAddress::operator!=(const PrivateAddress& rhs) const {
return !(*this == rhs);
}
const uint256& PrivateAddress::getEncryptionSecretKey() const {
return this->sk_enc;
}
const uint256& PrivateAddress::getAddressSecret() const {
return this->a_sk;
}
PublicAddress::PublicAddress() {
}
PublicAddress::PublicAddress(const uint256& a_pk, uint256& pk_enc) : a_pk(a_pk), pk_enc(pk_enc) {}
PublicAddress::PublicAddress(const PrivateAddress& addr_sk) {
std::vector<bool> a_sk_bool(ZC_A_SK_SIZE * 8);
std::vector<unsigned char> a_sk_v(addr_sk.getAddressSecret().begin(),
addr_sk.getAddressSecret().end());
convertBytesVectorToVector(a_sk_v, a_sk_bool);
std::vector<bool> zeros_256(256, 0);
std::vector<bool> a_pk_internal;
concatenateVectors(a_sk_bool, zeros_256, a_pk_internal);
std::vector<bool> a_pk_bool(ZC_A_PK_SIZE * 8);
hashVector(a_pk_internal, a_pk_bool);
std::vector<unsigned char> a_pk_vv(ZC_A_PK_SIZE);
convertVectorToBytesVector(a_pk_bool, a_pk_vv);
this->a_pk = uint256(a_pk_vv);
this->pk_enc = ZCNoteEncryption::generate_pubkey(addr_sk.getEncryptionSecretKey());
}
const uint256& PublicAddress::getEncryptionPublicKey() const {
return this->pk_enc;
}
const uint256& PublicAddress::getPublicAddressSecret() const {
return this->a_pk;
}
bool PublicAddress::operator==(const PublicAddress& rhs) const {
return ((this->a_pk == rhs.a_pk) && (this->pk_enc == rhs.pk_enc));
}
bool PublicAddress::operator!=(const PublicAddress& rhs) const {
return !(*this == rhs);
}
Address::Address(PrivateAddress& priv) : addr_pk(priv), addr_sk(priv) {
}
Address::Address() : addr_pk(), addr_sk() {
}
const PublicAddress& Address::getPublicAddress() const {
return this->addr_pk;
}
const PrivateAddress& Address::getPrivateAddress() const {
return this->addr_sk;
}
bool Address::operator==(const Address& rhs) const {
return ((this->addr_sk == rhs.addr_sk) && (this->addr_pk == rhs.addr_pk));
}
bool Address::operator!=(const Address& rhs) const {
return !(*this == rhs);
}
Address Address::CreateNewRandomAddress() {
std::vector<unsigned char> a_sk(ZC_A_SK_SIZE);
unsigned char a_sk_bytes[ZC_A_SK_SIZE];
getRandBytes(a_sk_bytes, ZC_A_SK_SIZE);
convertBytesToBytesVector(a_sk_bytes, a_sk);
uint256 a_sk_u(a_sk);
uint256 sk_enc = ZCNoteEncryption::generate_privkey(a_sk_u);
PrivateAddress addr_sk(a_sk_u, sk_enc);
return Address(addr_sk);
}
} /* namespace libzerocash */

View File

@ -1,87 +0,0 @@
/** @file
*****************************************************************************
Declaration of interfaces for the classes Address and PublicAddress.
*****************************************************************************
* @author This file is part of libzerocash, developed by the Zerocash
* project and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef ADDRESS_H_
#define ADDRESS_H_
#include <vector>
#include <string>
#include "uint256.h"
namespace libzerocash {
/***************************** Private address ********************************/
class PrivateAddress {
public:
/* This constructor is to be used ONLY for deserialization. */
PrivateAddress();
PrivateAddress(const uint256 &a_sk, const uint256 &sk_enc);
bool operator==(const PrivateAddress& rhs) const;
bool operator!=(const PrivateAddress& rhs) const;
const uint256& getAddressSecret() const;
const uint256& getEncryptionSecretKey() const;
private:
uint256 a_sk;
uint256 sk_enc;
};
/***************************** Public address ********************************/
class PublicAddress {
public:
/* This constructor is to be used ONLY for deserialization. */
PublicAddress();
PublicAddress(const uint256& a_pk, uint256& pk_enc);
PublicAddress(const PrivateAddress& addr_sk);
bool operator==(const PublicAddress& rhs) const;
bool operator!=(const PublicAddress& rhs) const;
const uint256& getPublicAddressSecret() const;
const uint256& getEncryptionPublicKey() const;
private:
uint256 a_pk;
uint256 pk_enc;
};
/******************************** Address ************************************/
class Address {
public:
/* This constructor is to be used ONLY for deserialization. */
Address();
Address(PrivateAddress&);
const PublicAddress& getPublicAddress() const;
const PrivateAddress& getPrivateAddress() const;
bool operator==(const Address& rhs) const;
bool operator!=(const Address& rhs) const;
static Address CreateNewRandomAddress();
private:
PublicAddress addr_pk;
PrivateAddress addr_sk;
};
} /* namespace libzerocash */
#endif /* ADDRESS_H_ */

View File

@ -1,137 +0,0 @@
/** @file
*****************************************************************************
Implementation of interfaces for the class Coin.
See coin.h .
*****************************************************************************
* @author This file is part of libzerocash, developed by the Zerocash
* project and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#include <stdexcept>
#include "Zerocash.h"
#include "Coin.h"
namespace libzerocash {
Coin::Coin(): addr_pk(), cm(), rho(ZC_RHO_SIZE), r(ZC_R_SIZE), coinValue(ZC_V_SIZE) {
}
Coin::Coin(const ZCNoteEncryption::Ciphertext& bucket,
Address& addr,
uint256& epk,
unsigned char nonce
): addr_pk(), cm(), rho(ZC_RHO_SIZE), r(ZC_R_SIZE), k(ZC_K_SIZE), coinValue(ZC_V_SIZE) {
ZCNoteDecryption decrypter(addr.getPrivateAddress().getEncryptionSecretKey());
auto plaintext = decrypter.decrypt(bucket,
epk,
uint256(),
nonce);
// Grab the byte vectors
std::vector<unsigned char> value_v(plaintext.begin(),
plaintext.begin() + ZC_V_SIZE);
std::vector<unsigned char> r_v(plaintext.begin() + ZC_V_SIZE,
plaintext.begin() + ZC_V_SIZE + ZC_R_SIZE);
std::vector<unsigned char> rho_v(plaintext.begin() + ZC_V_SIZE + ZC_R_SIZE,
plaintext.begin() + ZC_V_SIZE + ZC_R_SIZE + ZC_RHO_SIZE);
this->coinValue = value_v;
this->r = r_v;
this->rho = rho_v;
this->addr_pk = addr.getPublicAddress();
std::vector<unsigned char> a_pk(addr.getPublicAddress().getPublicAddressSecret().begin(),
addr.getPublicAddress().getPublicAddressSecret().end());
this->computeCommitments(a_pk);
}
Coin::Coin(const PublicAddress& addr, uint64_t value): addr_pk(addr), cm(), rho(ZC_RHO_SIZE), r(ZC_R_SIZE), k(ZC_K_SIZE), coinValue(ZC_V_SIZE)
{
convertIntToBytesVector(value, this->coinValue);
std::vector<unsigned char> a_pk(addr.getPublicAddressSecret().begin(),
addr.getPublicAddressSecret().end());
unsigned char rho_bytes[ZC_RHO_SIZE];
getRandBytes(rho_bytes, ZC_RHO_SIZE);
convertBytesToBytesVector(rho_bytes, this->rho);
unsigned char r_bytes[ZC_R_SIZE];
getRandBytes(r_bytes, ZC_R_SIZE);
convertBytesToBytesVector(r_bytes, this->r);
this->computeCommitments(a_pk);
}
Coin::Coin(const PublicAddress& addr, uint64_t value,
const std::vector<unsigned char>& rho, const std::vector<unsigned char>& r): addr_pk(addr), rho(rho), r(r), k(ZC_K_SIZE), coinValue(ZC_V_SIZE)
{
convertIntToBytesVector(value, this->coinValue);
std::vector<unsigned char> a_pk(addr.getPublicAddressSecret().begin(), addr.getPublicAddressSecret().end());
this->computeCommitments(a_pk);
}
void
Coin::computeCommitments(std::vector<unsigned char>& a_pk)
{
std::vector<unsigned char> k_internal;
std::vector<unsigned char> k_internalhash_trunc(16);
std::vector<unsigned char> k_internalhash_internal;
concatenateVectors(a_pk, this->rho, k_internalhash_internal);
std::vector<unsigned char> k_internalhash(ZC_K_SIZE);
hashVector(k_internalhash_internal, k_internalhash);
copy(k_internalhash.begin(), k_internalhash.begin()+16, k_internalhash_trunc.begin());
concatenateVectors(this->r, k_internalhash_trunc, k_internal);
hashVector(k_internal, this->k);
CoinCommitment com(this->coinValue, this->k);
this->cm = com;
}
bool Coin::operator==(const Coin& rhs) const {
return ((this->cm == rhs.cm) && (this->rho == rhs.rho) && (this->r == rhs.r) && (this->k == rhs.k) && (this->coinValue == rhs.coinValue) && (this->addr_pk == rhs.addr_pk));
}
bool Coin::operator!=(const Coin& rhs) const {
return !(*this == rhs);
}
const PublicAddress& Coin::getPublicAddress() const {
return this->addr_pk;
}
const CoinCommitment& Coin::getCoinCommitment() const {
return this->cm;
}
const std::vector<unsigned char>& Coin::getInternalCommitment() const {
return this->k;
}
const std::vector<unsigned char>& Coin::getRho() const {
return this->rho;
}
const std::vector<unsigned char>& Coin::getR() const {
return this->r;
}
uint64_t Coin::getValue() const {
return convertBytesVectorToInt(this->coinValue);
}
} /* namespace libzerocash */

View File

@ -1,76 +0,0 @@
/** @file
*****************************************************************************
Declaration of interfaces for the class Coin.
*****************************************************************************
* @author This file is part of libzerocash, developed by the Zerocash
* project and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef COIN_H_
#define COIN_H_
#include <vector>
#include "Address.h"
#include "CoinCommitment.h"
#include "zcash/NoteEncryption.hpp"
namespace libzerocash {
/********************************* Coin **************************************/
class Coin {
friend class MintTransaction;
friend class PourTransaction;
public:
/* This constructor is to be used ONLY for deserialization. */
Coin();
/**
* @param addr the address the coin will belong to when minted or poured into
* @param value the monetary value of the coin
*/
Coin(const PublicAddress& addr,
uint64_t value);
Coin(const PublicAddress& addr,
uint64_t value,
const std::vector<unsigned char>& rho,
const std::vector<unsigned char>& r);
Coin(const ZCNoteEncryption::Ciphertext&, Address& addr, uint256& epk, unsigned char nonce);
const PublicAddress& getPublicAddress() const;
const CoinCommitment& getCoinCommitment() const;
bool operator==(const Coin& rhs) const;
bool operator!=(const Coin& rhs) const;
uint64_t getValue() const;
const std::vector<unsigned char>& getRho() const;
const std::vector<unsigned char>& getR() const;
private:
PublicAddress addr_pk;
CoinCommitment cm;
std::vector<unsigned char> rho;
std::vector<unsigned char> r;
std::vector<unsigned char> k;
std::vector<unsigned char> coinValue;
const std::vector<unsigned char>& getInternalCommitment() const;
void computeCommitments(std::vector<unsigned char>& a_pk);
};
} /* namespace libzerocash */
#endif /* COIN_H_ */

View File

@ -1,58 +0,0 @@
/** @file
*****************************************************************************
Implementation of interfaces for the class CoinCommitment.
See CoinCommitment.h .
*****************************************************************************
* @author This file is part of libzerocash, developed by the Zerocash
* project and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#include <stdexcept>
#include <stdint.h>
#include "Zerocash.h"
#include "CoinCommitment.h"
namespace libzerocash {
CoinCommitment::CoinCommitment() : commitmentValue(ZC_CM_SIZE)
{ }
CoinCommitment::CoinCommitment(const std::vector<unsigned char>& val,
const std::vector<unsigned char>& k) : commitmentValue(ZC_CM_SIZE)
{
std::vector<bool> zeros_192(192, 0);
std::vector<bool> cm_internal;
std::vector<bool> value_bool(ZC_V_SIZE * 8, 0);
std::vector<bool> k_bool(ZC_K_SIZE * 8, 0);
if (val.size() > ZC_V_SIZE || k.size() > ZC_K_SIZE) {
throw std::runtime_error("CoinCommitment: inputs are too large");
}
libzerocash::convertBytesVectorToVector(val, value_bool);
libzerocash::convertBytesVectorToVector(k, k_bool);
libzerocash::concatenateVectors(k_bool, zeros_192, value_bool, cm_internal);
std::vector<bool> cm_bool(ZC_CM_SIZE * 8);
libzerocash::hashVector(cm_internal, cm_bool);
libzerocash::convertVectorToBytesVector(cm_bool, this->commitmentValue);
}
bool CoinCommitment::operator==(const CoinCommitment& rhs) const {
return (this->commitmentValue == rhs.commitmentValue);
}
bool CoinCommitment::operator!=(const CoinCommitment& rhs) const {
return !(*this == rhs);
}
const std::vector<unsigned char>& CoinCommitment::getCommitmentValue() const {
return this->commitmentValue;
}
} /* namespace libzerocash */

View File

@ -1,44 +0,0 @@
/** @file
*****************************************************************************
Declaration of interfaces for the class CoinCommitment.
*****************************************************************************
* @author This file is part of libzerocash, developed by the Zerocash
* project and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef COINCOMMITMENT_H_
#define COINCOMMITMENT_H_
#include <vector>
namespace libzerocash {
/****************************** Coin commitment ******************************/
class CoinCommitment {
friend class PourTransaction;
friend class PourProver;
public:
CoinCommitment();
CoinCommitment(const std::vector<unsigned char>& val,
const std::vector<unsigned char>& k);
const std::vector<unsigned char>& getCommitmentValue() const;
bool operator==(const CoinCommitment& rhs) const;
bool operator!=(const CoinCommitment& rhs) const;
private:
std::vector<unsigned char> commitmentValue;
};
} /* namespace libzerocash */
#endif /* COINCOMMITMENT_H_ */

View File

@ -1,42 +0,0 @@
/** @file
*****************************************************************************
Functionality to generate files containing the Zerocash public parameters.
*****************************************************************************
* @author This file is part of libzerocash, developed by the Zerocash
* project and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#include <fstream>
#include "Zerocash.h"
#include "ZerocashParams.h"
#include "libsnark/common/default_types/r1cs_ppzksnark_pp.hpp"
#include "libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp"
using namespace libzerocash;
int main(int argc, char **argv)
{
if(argc != 4) {
std::cerr << "Usage: " << argv[0] << " treeDepth provingKeyFileName verificationKeyFileName" << std::endl;
return 1;
}
unsigned int tree_depth = atoi(argv[1]);
std::string pkFile = argv[2];
std::string vkFile = argv[3];
auto keypair = libzerocash::ZerocashParams::GenerateNewKeyPair(tree_depth);
libzerocash::ZerocashParams p(
tree_depth,
&keypair
);
libzerocash::ZerocashParams::SaveProvingKeyToFile(&p.getProvingKey(), pkFile);
libzerocash::ZerocashParams::SaveVerificationKeyToFile(&p.getVerificationKey(), vkFile);
return 0;
}

View File

@ -1,606 +0,0 @@
/** @file
*****************************************************************************
Implementation of interfaces for the classes IncrementalMerkleTreeCompact,
IncrementalMerkleNode, and IncrementalMerkleTree.
See IncrementalMerkleTree.h .
*****************************************************************************
* @author This file is part of libzerocash, developed by the Zerocash
* project and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#include "IncrementalMerkleTree.h"
#include "Zerocash.h"
#include <cmath>
#include <iostream>
#include <vector>
namespace libzerocash {
/////////////////////////////////////////////
// IncrementalMerkleTreeCompact class
/////////////////////////////////////////////
std::vector<unsigned char> IncrementalMerkleTreeCompact::serialize() const {
/* Serialization format:
* treeHeight (4 bytes, big endian)
* hashList (ceil(treeHeight / 8) bytes)
* hashVec (32 bytes for every 1 bit in hashList)
*/
std::vector<unsigned char> serialized;
/* treeHeight (4 bytes, big endian) */
std::vector<unsigned char> treeHeightBytes(4);
convertIntToBytesVector((uint64_t)this->treeHeight, treeHeightBytes);
serialized.insert(serialized.end(), treeHeightBytes.begin(), treeHeightBytes.end());
/* hashList */
assert(this->hashList.size() == this->treeHeight);
/* Pad it out to a multiple of 8 bits. */
std::vector<bool> hashList = this->hashList;
if (hashList.size() % 8 != 0) {
hashList.insert(hashList.begin(), 8 - (hashList.size() % 8), false);
}
assert(hashList.size() % 8 == 0);
/* Convert it to a byte vector. */
std::vector<unsigned char> hashListBytes(hashList.size() / 8);
convertVectorToBytesVector(hashList, hashListBytes);
serialized.insert(serialized.end(), hashListBytes.begin(), hashListBytes.end());
/* hashVec */
assert(this->hashVec.size() == countOnes(this->hashList));
for (uint32_t i = 0; i < this->hashVec.size(); i++) {
assert(this->hashVec.at(i).size() == 32);
serialized.insert(serialized.end(), this->hashVec.at(i).begin(), this->hashVec.at(i).end());
}
return serialized;
}
IncrementalMerkleTreeCompact IncrementalMerkleTreeCompact::deserialize(const std::vector<unsigned char>& serialized) {
IncrementalMerkleTreeCompact deserialized;
size_t currentPos = 0;
/* treeHeight */
std::vector<unsigned char> treeHeightBytes = vectorSlice(serialized, 0, 4);
currentPos += 4;
deserialized.treeHeight = convertBytesVectorToInt(treeHeightBytes);
/* hashList */
uint32_t hashListBytesLength = ceil(deserialized.treeHeight / 8.0);
std::vector<unsigned char> hashListBytes = vectorSlice(serialized, currentPos, hashListBytesLength);
currentPos += hashListBytesLength;
convertBytesVectorToVector(hashListBytes, deserialized.hashList);
/* Remove the multiple-of-8-bits padding. */
deserialized.hashList.erase(deserialized.hashList.begin(),
deserialized.hashList.end() - deserialized.treeHeight
);
/* hashVec */
size_t hashVecSize = countOnes(deserialized.hashList);
for (size_t i = 0; i < hashVecSize; i++) {
std::vector<unsigned char> hashVecElement = vectorSlice(serialized, currentPos, 32);
currentPos += 32;
deserialized.hashVec.push_back(hashVecElement);
}
if (currentPos != serialized.size()) {
throw std::runtime_error("Serialized vector is longer than expected.");
}
return deserialized;
}
/////////////////////////////////////////////
// IncrementalMerkleTree class
/////////////////////////////////////////////
// Custom tree constructor (initialize tree of specified height)
IncrementalMerkleTree::IncrementalMerkleTree(uint32_t height) : root(0, height) {
treeHeight = height;
}
// Vector constructor. Initializes and inserts a list of elements.
IncrementalMerkleTree::IncrementalMerkleTree(std::vector< std::vector<bool> > &valueVector, uint32_t height) : root(0, height)
{
// Initialize the tree
treeHeight = height;
// Load the tree with all the given values
if (this->insertVector(valueVector) == false) {
throw std::runtime_error("Could not insert vector into Merkle Tree: too many elements");
}
}
// Custom tree constructor (initialize tree from compact representation)
//
IncrementalMerkleTree::IncrementalMerkleTree(IncrementalMerkleTreeCompact &compact) : root(0, 0)
{
// Initialize the tree
this->treeHeight = compact.getHeight();
root.treeHeight = treeHeight;
// Reconstitute tree from compact representation
this->fromCompactRepresentation(compact);
}
bool
IncrementalMerkleTree::insertElement(const std::vector<bool> &hashV, std::vector<bool> &index) {
// Resize the index vector
index.resize(this->treeHeight);
// Insert the element
return this->root.insertElement(hashV, index);
}
bool
IncrementalMerkleTree::insertElement(const std::vector<unsigned char> &hashV, std::vector<unsigned char> &index) {
// Create a temporary vector to hold hashV
std::vector<bool> hashVBool(hashV.size() * 8);
convertBytesVectorToVector(hashV, hashVBool);
// Create a temporary vector to hold the index
std::vector<bool> indexBool(this->treeHeight, 0);
// Insert the element
bool result = this->insertElement(hashVBool, indexBool);
// Convert the returned vector
index.resize(index.size() / 8); // this might need to include a ceil
convertVectorToBytesVector(indexBool, index);
return result;
}
bool
IncrementalMerkleTree::getWitness(const std::vector<bool> &index, merkle_authentication_path &witness) {
// Resize the witness if necessary
if (witness.size() < this->treeHeight) {
witness.resize(treeHeight);
}
std::vector<bool> indexPadded = index;
// Discard leading bits of the index if necessary
if (indexPadded.size() > this->treeHeight) {
indexPadded.erase(indexPadded.begin(), indexPadded.begin() + (indexPadded.size() - this->treeHeight));
}
// If the index vector is less than 'treeHeight' bits, pad the leftmost bits with 0 (false)
// This is to deal with the situation where somebody encodes e.g., a 32-bit integer as an index
// into a 64 height tree and does not explicitly pad to length.
if (indexPadded.size() < this->treeHeight) {
indexPadded.insert(indexPadded.begin(), (this->treeHeight - 1) - indexPadded.size(), false);
}
return this->root.getWitness(indexPadded, witness);
}
bool
IncrementalMerkleTree::insertVector(std::vector< std::vector<bool> > &valueVector)
{
std::vector<bool> index;
for (std::vector< std::vector<bool> >::iterator iter = valueVector.begin();
iter != valueVector.end(); ++iter) {
if (this->insertElement(*iter, index) == false) {
return false;
}
}
return true;
}
bool
IncrementalMerkleTree::getRootValue(std::vector<bool>& r) const {
// Query the root for its hash
this->root.getValue(r);
return true;
}
bool
IncrementalMerkleTree::getRootValue(std::vector<unsigned char>& r) const {
// Create a temporary byte vector
std::vector<bool> tempR(r.size() * 8, 0);
// Query the root for its hash
this->root.getValue(tempR);
// Convert the result back into the given vector
convertVectorToBytesVector(tempR, r);
return true;
}
std::vector<unsigned char>
IncrementalMerkleTree::getRoot(){
std::vector<unsigned char> temp(8);
this->getRootValue(temp);
return temp;
}
bool
IncrementalMerkleTree::prune()
{
return this->root.prune();
}
IncrementalMerkleTreeCompact
IncrementalMerkleTree::getCompactRepresentation() const
{
IncrementalMerkleTreeCompact rep;
rep.hashList.resize(this->treeHeight);
rep.treeHeight = this->treeHeight;
std::fill (rep.hashList.begin(), rep.hashList.end(), false);
this->root.getCompactRepresentation(rep);
return rep;
}
bool
IncrementalMerkleTree::fromCompactRepresentation(IncrementalMerkleTreeCompact &rep)
{
return this->root.fromCompactRepresentation(rep, 0);
}
/////////////////////////////////////////////
// IncrementalMerkleNode class
/////////////////////////////////////////////
// Standard constructor
//
IncrementalMerkleNode::IncrementalMerkleNode(uint32_t depth, uint32_t height) : left(NULL), right(NULL), value(CSHA256::OUTPUT_SIZE * 8, 0), nodeDepth(depth), treeHeight(height),
subtreeFull(false), subtreePruned(false)
{
}
// Copy constructor
//
IncrementalMerkleNode::IncrementalMerkleNode(const IncrementalMerkleNode& toCopy) : left(NULL), right(NULL), value(CSHA256::OUTPUT_SIZE * 8, 0)
{
this->nodeDepth = toCopy.nodeDepth;
this->subtreePruned = toCopy.subtreePruned;
this->subtreeFull = toCopy.subtreeFull;
this->value = toCopy.value;
this->treeHeight = toCopy.treeHeight;
// Recursively copy the subtrees
if (toCopy.left) {
this->left = new IncrementalMerkleNode(toCopy.left->nodeDepth, toCopy.left->treeHeight);
*(this->left) = *(toCopy.left);
}
if (toCopy.right) {
this->right = new IncrementalMerkleNode(toCopy.right->nodeDepth, toCopy.right->treeHeight);
*(this->right) = *(toCopy.right);
}
}
IncrementalMerkleNode::~IncrementalMerkleNode()
{
if (this->left) {
delete this->left;
this->left = NULL;
}
if (this->right) {
delete this->right;
this->right = NULL;
}
}
bool
IncrementalMerkleNode::insertElement(const std::vector<bool> &hashV, std::vector<bool> &index)
{
bool result = false;
// Check if we have any free leaves. If not, bail.
if (this->subtreeFull == true) {
return false;
}
// Are we a leaf? If so, store the hash value.
if (this->isLeaf()) {
// Store the given hash value here and return success.
this->value = hashV;
this->subtreeFull = true;
return true;
}
// We're not a leaf. Try to insert into subtrees, creating them if necessary.
// Try to recurse on left subtree
if (!this->left) {
this->left = new IncrementalMerkleNode(this->nodeDepth + 1, this->treeHeight);
}
result = this->left->insertElement(hashV, index);
if (result == true) {
// Update the index value to indicate where the new node went
index.at(this->nodeDepth) = false;
}
// If that failed, try to recurse on right subtree.
if (result == false) {
if (!this->right) {
this->right = new IncrementalMerkleNode(this->nodeDepth + 1, this->treeHeight);
}
result = this->right->insertElement(hashV, index);
if (result == true) {
index.at(this->nodeDepth) = true;
}
}
// If one of the inserts succeeded, update our 'fullness' status.
if (result == true) {
this->updateHashValue();
if (this->isLeaf()) { this->subtreeFull = true; }
else {
this->subtreeFull = this->checkIfNodeFull();
}
}
// Return the result
return result;
}
bool
IncrementalMerkleNode::getWitness(const std::vector<bool> &index, merkle_authentication_path &witness)
{
bool result = false;
// If this node is a leaf: do nothing and return success
if (this->isLeaf()) {
return true;
}
// If this node is pruned, we can't fetch a witness. Return failure.
if (this->isPruned()) {
return false;
}
// If the index path leads to the left, we grab the hash value on the
// right -- then recurse on the left node.
if (index.at(nodeDepth) == false) {
// Make sure there is a value on the right. If not we put the 'null' hash (0) into that element.
if (this->right == NULL) {
witness.at(nodeDepth).resize(CSHA256::OUTPUT_SIZE * 8);
std::fill (witness.at(nodeDepth).begin(), witness.at(nodeDepth).end(), false);
} else {
this->right->getValue(witness.at(nodeDepth));
//printVectorAsHex(witness.at(nodeDepth));
}
// Recurse on the left node
if (this->left) {
result = this->left->getWitness(index, witness);
}
}
// If the index path leads to the right, we grab the hash value on the
// left -- then recurse on the right node.
if (index.at(nodeDepth) == true) {
this->left->getValue(witness.at(nodeDepth));
// Recurse on the right node
if (this->right) {
result = this->right->getWitness(index, witness);
}
}
return result;
}
bool
IncrementalMerkleNode::prune()
{
bool result = true;
// If we're already pruned, return.
if (this->isPruned() == true) {
return true;
}
// Check to see if this node is full. If so, delete the subtrees.
if (this->subtreeFull == true) {
if (this->left) {
delete this->left;
this->left = NULL;
}
if (this->right) {
delete this->right;
this->right = NULL;
}
this->subtreePruned = true;
} else {
// Node is not full. Recurse on left and right.
if (this->left) {
result &= this->left->prune();
}
if (this->right) {
result &= this->right->prune();
}
}
return result;
}
void
IncrementalMerkleNode::updateHashValue()
{
// Take no action on leaves or pruned nodes.
if (this->isLeaf() || this->isPruned()) {
return;
}
// Obtain the hash of the two subtrees and hash the
// concatenation of the two.
std::vector<bool> hash(CSHA256::OUTPUT_SIZE * 8);
std::vector<bool> zero(CSHA256::OUTPUT_SIZE * 8);
std::fill (zero.begin(), zero.end(), false);
// The following code is ugly and should be refactored. It runs
// four special cases depending on whether left/right is NULL.
// It also ensures that the "hash" of (0 || 0) is 0.
if (this->left && !(this->right)) {
if (VectorIsZero(this->left->getValue())) {
hash = zero;
} else {
hashVectors(this->left->getValue(), zero, hash);
}
} else if (!(this->left) && this->right) {
if (VectorIsZero(this->right->getValue())) {
hash = zero;
} else {
hashVectors(zero, this->left->getValue(), hash);
}
} else if (this->left && this->right) {
if (VectorIsZero(this->left->getValue()) && VectorIsZero(this->right->getValue())) {
hash = zero;
} else {
hashVectors(this->left->getValue(), this->right->getValue(), hash);
}
} else {
hash = zero;
}
this->value = hash;
}
bool
IncrementalMerkleNode::checkIfNodeFull()
{
if (this->isPruned()) {
return true;
}
if (this->left == NULL || this->right == NULL) {
return false;
}
return (this->left->subtreeFull && this->right->subtreeFull);
}
void
IncrementalMerkleNode::getCompactRepresentation(IncrementalMerkleTreeCompact &rep) const
{
// Do nothing at the bottom level
if (this->isLeaf()) {
return;
}
// There's no content below us. We're done
if (!this->left) {
return;
}
// If we have no right elements, don't include any hashes. Recurse to the left.
if (this->hasRightChildren() == false && this->left->isLeaf() == false && this->left->subtreeFull == false) {
rep.hashList.at(this->nodeDepth) = false;
this->left->getCompactRepresentation(rep);
return;
}
// Otherwise: Add our left child hash to the tree.
rep.hashList.at(this->nodeDepth) = true;
std::vector<unsigned char> hash(CSHA256::OUTPUT_SIZE, 0);
convertVectorToBytesVector(this->left->getValue(), hash);
rep.hashVec.push_back(hash);
// If we have a right child, recurse to the right
if (this->hasRightChildren()) {
this->right->getCompactRepresentation(rep);
return;
}
// We get here in one of the following cases:
// 1. Our left child is a leaf, and there's no right child.
// 2. Our left child is a full tree, and there's no right child.
// We've gone right for the last time, now we go left until we reach the
// bottom.
for (uint32_t i = this->nodeDepth + 1; i < this->treeHeight; i++) {
rep.hashList.at(i) = false;
}
}
bool
IncrementalMerkleNode::fromCompactRepresentation(IncrementalMerkleTreeCompact &rep, uint32_t pos)
{
bool result = false;
// Do nothing at the bottom level
if (this->isLeaf()) {
return true;
}
// If we have any subtrees (or this tree already has stuff in it), clean it out.
if (this->left) {
// XXX memory leak: left might have the only pointers to its heap
// allocated children!
delete this->left;
this->left = NULL;
}
if (this->right) {
// XXX memory leak: right might have the only pointers to its heap
// allocated children!
delete this->right;
this->right = NULL;
}
this->subtreeFull = this->subtreePruned = false;
// If the hashList[nodeDepth] is true, insert the next hash into the left tree
// and mark it full AND pruned. Then recurse to the right.
if (rep.hashList.at(this->nodeDepth) == true) {
// Create a left node
this->left = new IncrementalMerkleNode(this->nodeDepth + 1, this->treeHeight);
// Fill the left node with the value and mark it full/pruned
std::vector<bool> hash(CSHA256::OUTPUT_SIZE * 8, 0);
convertBytesVectorToVector(rep.hashVec.at(pos), hash);
this->left->value = hash;
this->left->subtreePruned = this->left->subtreeFull = true;
// Create a right node and recurse on it (incrementing pos)
this->right = new IncrementalMerkleNode(this->nodeDepth + 1, this->treeHeight);
result = this->right->fromCompactRepresentation(rep, pos + 1);
} else if (this->nodeDepth < (this->treeHeight - 1)) {
// Otherwise --
// * If we're about to create a leaf level, do nothing.
// * Else create a left node and recurse on it.
this->left = new IncrementalMerkleNode(this->nodeDepth + 1, this->treeHeight);
// Otherwise recurse on the left node. Do not increment pos.
result = this->left->fromCompactRepresentation(rep, pos);
}
// Update the hash value of this node
this->updateHashValue();
return result;
}
IncrementalMerkleNode
IncrementalMerkleNode::operator=(const IncrementalMerkleNode &rhs) {
IncrementalMerkleNode dup(rhs);
return dup;
}
} /* namespace libzerocash */

View File

@ -1,140 +0,0 @@
/** @file
*****************************************************************************
Declaration of interfaces for the classes IncrementalMerkleTreeCompact,
IncrementalMerkleNode, and IncrementalMerkleTree.
*****************************************************************************
* @author This file is part of libzerocash, developed by the Zerocash
* project and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef INCREMENTALMERKLETREE_H_
#define INCREMENTALMERKLETREE_H_
#include "crypto/sha256.h"
#include "Zerocash.h"
#include <vector>
#include <iostream>
#include <map>
#include <cstring>
#include "libsnark/common/data_structures/merkle_tree.hpp"
namespace libzerocash {
/******************* Incremental Merkle tree compact *************************/
/* This is a comapct way to represent an incremental merkle tree, where all full
* subtrees are replaced by their hashes. It contains just enough information
* that you can continue addding elements to the tree.
*
* This class can only be constructed by IncrementalMerkleTree, and after that,
* it is immutable. To act on a compact representation, it must first be
* de-compactified by loading it into an IncrementalMerkleTree.
*/
class IncrementalMerkleTreeCompact {
friend class IncrementalMerkleTree;
friend class IncrementalMerkleNode;
public:
uint32_t getHeight() { return this->treeHeight; }
uint32_t getTreeHeight() { return treeHeight; }
std::vector< std::vector<unsigned char> > const& getHashVec() { return hashVec; }
std::vector< bool > const& getHashList() { return hashList; }
std::vector<unsigned char> serialize() const;
static IncrementalMerkleTreeCompact deserialize(const std::vector<unsigned char>& serialized);
private:
IncrementalMerkleTreeCompact() : treeHeight(0) {}
uint32_t treeHeight;
std::vector< std::vector<unsigned char> > hashVec;
std::vector< bool > hashList;
};
/********************* Incremental Merkle tree node **************************/
class IncrementalMerkleNode {
public:
CSHA256 ctx256;
IncrementalMerkleNode* left;
IncrementalMerkleNode* right;
std::vector<bool> value;
uint32_t nodeDepth;
uint32_t treeHeight;
bool subtreeFull;
bool subtreePruned;
IncrementalMerkleNode(uint32_t depth, uint32_t height);
IncrementalMerkleNode(const IncrementalMerkleNode& toCopy);
~IncrementalMerkleNode();
// Methods
bool insertElement(const std::vector<bool> &hashV, std::vector<bool> &index);
bool getWitness(const std::vector<bool> &index, merkle_authentication_path &witness);
bool prune();
void getCompactRepresentation(IncrementalMerkleTreeCompact &rep) const;
bool fromCompactRepresentation(IncrementalMerkleTreeCompact &rep, uint32_t pos);
// Utility methods
bool isLeaf() const { return (nodeDepth == treeHeight); }
bool isPruned() const { return subtreePruned; }
bool hasFreeLeaves() const { return (!subtreeFull); }
bool hasRightChildren() const { if (!right) return false; return true; }
void getValue(std::vector<bool> &r) const { r = value; }
const std::vector<bool>& getValue() const { return value; }
bool checkIfNodeFull();
void updateHashValue();
IncrementalMerkleNode operator=(const IncrementalMerkleNode &rhs);
};
/************************ Incremental Merkle tree ****************************/
class IncrementalMerkleTree {
protected:
IncrementalMerkleNode root;
uint32_t treeHeight;
public:
IncrementalMerkleTree(uint32_t height = ZEROCASH_DEFAULT_TREE_SIZE);
IncrementalMerkleTree(std::vector< std::vector<bool> > &valueVector, uint32_t height);
IncrementalMerkleTree(IncrementalMerkleTreeCompact &compact);
void setTo(const IncrementalMerkleTree &other) {
auto compact = other.getCompactRepresentation();
fromCompactRepresentation(compact);
}
bool insertElement(const std::vector<bool> &hashV, std::vector<bool> &index);
bool insertElement(const std::vector<unsigned char> &hashV, std::vector<unsigned char> &index);
bool insertVector(std::vector< std::vector<bool> > &valueVector);
bool getWitness(const std::vector<bool> &index, merkle_authentication_path &witness);
bool getRootValue(std::vector<bool>& r) const;
bool getRootValue(std::vector<unsigned char>& r) const;
std::vector<unsigned char>getRoot();
bool prune();
IncrementalMerkleTreeCompact getCompactRepresentation() const;
std::vector<unsigned char> serialize() const {
auto compact = getCompactRepresentation();
return compact.serialize();
}
static IncrementalMerkleTree deserialize(const std::vector<unsigned char>& serialized) {
auto deserialized = IncrementalMerkleTreeCompact::deserialize(serialized);
return IncrementalMerkleTree(deserialized);
}
bool fromCompactRepresentation(IncrementalMerkleTreeCompact &rep);
};
} /* namespace libzerocash */
#endif /* INCREMENTALMERKLETREE_H_ */

View File

@ -1,161 +0,0 @@
OPTFLAGS = -march=native -mtune=native -O2
CXXFLAGS += -g -Wall -Wextra -Wno-unused-parameter -std=c++11 -fPIC -Wno-unused-variable
LDFLAGS += -flto
DEPSRC=depsrc
DEPINST=depinst
LIBZEROCASH=libzerocash
UTILS=$(LIBZEROCASH)/utils
TESTUTILS=tests
LDLIBS += -L $(DEPINST)/lib -Wl,-rpath $(DEPINST)/lib -L . -lsnark -lgmpxx -lgmp
ifeq ($(USE_MT),1)
LDLIBS += -lboost_system-mt
LDLIBS += -lboost_unit_test_framework-mt
else
LDLIBS += -lboost_system
LDLIBS += -lboost_unit_test_framework
endif
LDLIBS += -lcrypto -lcryptopp -lz -ldl
ifeq ($(LINK_RT),1)
LDLIBS += -lrt
endif
CXXFLAGS += -I $(DEPINST)/include -I $(DEPINST)/include/libsnark -I . -DUSE_ASM -DCURVE_ALT_BN128
LIBPATH = /usr/local/lib
SRCS= \
$(UTILS)/sha256.cpp \
$(UTILS)/util.cpp \
$(LIBZEROCASH)/IncrementalMerkleTree.cpp \
$(LIBZEROCASH)/Address.cpp \
$(LIBZEROCASH)/CoinCommitment.cpp \
$(LIBZEROCASH)/Coin.cpp \
$(LIBZEROCASH)/MintTransaction.cpp \
$(LIBZEROCASH)/PourInput.cpp \
$(LIBZEROCASH)/PourOutput.cpp \
$(LIBZEROCASH)/PourProver.cpp \
$(LIBZEROCASH)/PourTransaction.cpp \
$(LIBZEROCASH)/ZerocashParams.cpp \
$(TESTUTILS)/timer.cpp
EXECUTABLES= \
zerocash_pour_ppzksnark/tests/test_zerocash_pour_ppzksnark \
zerocash_pour_ppzksnark/profiling/profile_zerocash_pour_gadget \
tests/zerocashTest \
tests/utilTest \
tests/merkleTest \
libzerocash/GenerateParamsForFiles
OBJS=$(patsubst %.cpp,%.o,$(SRCS))
ifeq ($(MINDEPS),1)
CXXFLAGS += -DMINDEPS
else
LDLIBS += -lboost_program_options
LDLIBS += -lprocps
endif
ifeq ($(LOWMEM),1)
CXXFLAGS += -DLOWMEM
endif
ifeq ($(STATIC),1)
CXXFLAGS += -static -DSTATIC
endif
ifeq ($(PROFILE_CURVE),1)
CXXFLAGS += -static -DPROFILE_CURVE
endif
ifeq ($(MULTICORE),1)
# When running ./get-libsnark to prepare for this build, use:
# $ LIBSNARK_FLAGS='MULTICORE=1 STATIC=1' ./get-libsnark.
# If you're missing some static libraries, it may help to also add
# $ NO_PROCPS=1 ... ./get-libsnark
# and pass MINDEPS=1 to this makefile
# and run ./get-cryptopp to build the static cryptopp library.
CXXFLAGS += -static -fopenmp -DMULTICORE
endif
all: $(EXECUTABLES) libzerocash.a
cppdebug: CXXFLAGS += -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC
cppdebug: debug
debug: CXXFLAGS += -DDEBUG -g -ggdb3
debug: all
noasserts: CXXFLAGS += -DNDEBUG -Wno-unused-variable -Wno-unused-but-set-variable
noasserts: all
# In order to detect changes to #include dependencies. -MMD below generates a .d file for .cpp file. Include the .d file.
-include $(SRCS:.cpp=.d)
$(OBJS) ${patsubst %,%.o,${EXECUTABLES}}: %.o: %.cpp
$(CXX) -o $@ $< -c -MMD $(CXXFLAGS)
$(EXECUTABLES): %: %.o $(OBJS)
$(CXX) -o $@ $^ $(CXXFLAGS) $(LDFLAGS) $(LDLIBS)
libzerocash: $(OBJS) $(USER_OBJS)
@echo 'Building target: $@'
@echo 'Invoking: Cross G++ Linker'
$(CXX) -shared -o "libzerocash.so" $(OBJS) $(CXXFLAGS) $(LDFLAGS) $(LDLIBS)
@echo 'Finished building target: $@'
@echo 'Copying libzerocash.so'
sudo cp libzerocash.so $(LIBPATH)/libzerocash.so
sudo ldconfig
@echo 'Finished copying libzerocash.so'
@echo ' '
libzerocash.a: $(OBJS) $(USER_OBJS)
@echo 'Building target: $@'
@echo 'Invoking: Cross G++ Linker'
$(AR) rcvs $@ $(OBJS)
@echo 'Finished building target: $@'
#@echo 'Copying libzerocash.a'
#sudo cp libzerocash.a $(LIBPATH)/libzerocash.a
#sudo ldconfig
#@echo 'Finished copying libzerocash.a'
@echo ' '
test_library: %: tests/zerocashTest.o $(OBJS)
$(CXX) -o tests/$@ $^ $(CXXFLAGS) $(LDFLAGS) $(LDLIBS) -lzerocash
banktest_library: %: bankTest.o $(OBJS)
$(CXX) -o $@ $^ $(CXXFLAGS) $(LDFLAGS) $(LDLIBS) -lzerocash
merkletest_library: %: merkleTest.o $(OBJS)
$(CXX) -o $@ $^ $(CXXFLAGS) $(LDFLAGS) $(LDLIBS) -lzerocash
.PHONY: clean install
clean:
$(RM) \
$(OBJS) \
$(EXECUTABLES) \
${patsubst %,%.o,${EXECUTABLES}} \
${patsubst %,%.d,${EXECUTABLES}} \
${patsubst %.cpp,%.d,${SRCS}} \
libzerocash.a \
tests/test_library
HEADERS_SRC=$(shell find . -name '*.hpp' -o -name '*.tcc' -o -name '*.h')
HEADERS_DEST=$(patsubst %,$(PREFIX)/include/libzerocash/%,$(HEADERS_SRC))
$(HEADERS_DEST): $(PREFIX)/include/libzerocash/%: %
mkdir -p $(shell dirname $@)
cp $< $@
install: all $(HEADERS_DEST)
mkdir -p $(PREFIX)/lib
install -m 0755 libzerocash.a $(PREFIX)/lib/
mkdir -p $(PREFIX)/bin
install -m 0755 -t $(PREFIX)/bin/ $(EXECUTABLES)

View File

@ -1,75 +0,0 @@
/** @file
*****************************************************************************
Implementation of interfaces for the class MintTransaction.
See MintTransaction.h .
*****************************************************************************
* @author This file is part of libzerocash, developed by the Zerocash
* project and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#include "Zerocash.h"
#include "MintTransaction.h"
namespace libzerocash {
MintTransaction::MintTransaction(): coinValue(0), internalCommitment(), externalCommitment()
{ }
/**
* Creates a transaction minting the coin c.
*
* @param c the coin to mint.
*/
MintTransaction::MintTransaction(const Coin& c): coinValue(ZC_V_SIZE)
{
convertIntToBytesVector(c.getValue(), this->coinValue);
internalCommitment = c.getInternalCommitment();
externalCommitment = c.getCoinCommitment();
}
/**
* Verify the correctness of a Mint transaction.
*
* @return true if correct, false otherwise.
*/
bool MintTransaction::verify() const{
// Check that the internal commitment is the right size
if (this->internalCommitment.size() != ZC_K_SIZE) {
return false;
}
// The external commitment should formulated as:
// H( internalCommitment || 0^192 || coinValue)
//
// To check the structure of our proof we simply reconstruct
// a version of the external commitment and check that it's
// equal to the value we store.
//
// We use the constructor for CoinCommitment to do this.
try {
CoinCommitment comp(this->coinValue, this->internalCommitment);
return (comp == this->externalCommitment);
} catch (std::runtime_error) {
return false;
}
return false;
}
const CoinCommitmentValue& MintTransaction::getMintedCoinCommitmentValue() const{
return this->externalCommitment.getCommitmentValue();
}
uint64_t MintTransaction::getMonetaryValue() const {
return convertBytesVectorToInt(this->coinValue);
}
} /* namespace libzerocash */

View File

@ -1,69 +0,0 @@
/** @file
*****************************************************************************
Declaration of interfaces for the class MintTransaction.
*****************************************************************************
* @author This file is part of libzerocash, developed by the Zerocash
* project and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef MINTTRANSACTION_H_
#define MINTTRANSACTION_H_
#include "CoinCommitment.h"
#include "Coin.h"
typedef std::vector<unsigned char> CoinCommitmentValue;
namespace libzerocash{
/***************************** Mint transaction ******************************/
class MintTransaction {
public:
MintTransaction();
/**
* Creates a transaction minting the provided coin.
*
* @param c the coin to mint.
*/
MintTransaction(const Coin& c);
/**
* Verifies the MintTransaction.
* In particular, this checks that output coin commitment
* actually is to a coin of the claimed value.
*
* @return true if valid, false otherwise.
*/
bool verify() const;
/**
*Gets the commitment to the coin that was minted by this transaction.
*
* @return the commitment
*/
const CoinCommitmentValue& getMintedCoinCommitmentValue() const;
/**
* Gets the monetary value of the minted coin.
*
* @return the value
*/
uint64_t getMonetaryValue() const;
private:
std::vector<unsigned char> coinValue; // coin value
std::vector<unsigned char> internalCommitment; // "k" in paper notation
CoinCommitment externalCommitment; // "cm" in paper notation
const CoinCommitment& getCoinCommitment() const { return this->externalCommitment; }
};
} /* namespace libzerocash */
#endif /* MINTTRANSACTION_H_ */

View File

@ -1,39 +0,0 @@
/** @file
*****************************************************************************
Implementation of interfaces for the class PourInput.
See PourInput.h .
*****************************************************************************
* @author This file is part of libzerocash, developed by the Zerocash
* project and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#include "PourInput.h"
namespace libzerocash {
PourInput::PourInput(int tree_depth): old_coin(), merkle_index(), path() {
this->old_address = Address::CreateNewRandomAddress();
this->old_coin = Coin(this->old_address.getPublicAddress(), 0);
ZCIncrementalMerkleTree merkleTree;
merkleTree.append(uint256(this->old_coin.getCoinCommitment().getCommitmentValue()));
auto witness = merkleTree.witness();
auto merkle_path = witness.path();
this->path = merkle_path.authentication_path;
this->merkle_index = convertVectorToInt(merkle_path.index);
}
PourInput::PourInput(Coin old_coin,
Address old_address,
const libzcash::MerklePath &path) : old_address(old_address), old_coin(old_coin), path(path.authentication_path) {
this->merkle_index = convertVectorToInt(path.index);
};
} /* namespace libzerocash */

View File

@ -1,38 +0,0 @@
/** @file
*****************************************************************************
Declaration of interfaces for the class PourInput.
*****************************************************************************
* @author This file is part of libzerocash, developed by the Zerocash
* project and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef POURINPUT_H_
#define POURINPUT_H_
#include "Coin.h"
#include "ZerocashParams.h"
#include "zcash/IncrementalMerkleTree.hpp"
namespace libzerocash {
class PourInput {
public:
PourInput(int tree_depth);
PourInput(Coin old_coin,
Address old_address,
const libzcash::MerklePath& path);
Coin old_coin;
Address old_address;
size_t merkle_index;
merkle_authentication_path path;
};
} /* namespace libzerocash */
#endif /* POURINPUT_H_ */

View File

@ -1,29 +0,0 @@
/** @file
*****************************************************************************
Implementation of interfaces for the class PourOutput.
See PourOutput.h .
*****************************************************************************
* @author This file is part of libzerocash, developed by the Zerocash
* project and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#include "PourOutput.h"
namespace libzerocash {
PourOutput::PourOutput(uint64_t val) {
Address dummy_to_address = Address::CreateNewRandomAddress();
this->to_address = dummy_to_address.getPublicAddress();
this->new_coin = Coin(dummy_to_address.getPublicAddress(), val);
}
PourOutput::PourOutput(const Coin new_coin,
const PublicAddress to_address) : new_coin(new_coin), to_address(to_address) {
}
} /* namespace libzerocash */

View File

@ -1,32 +0,0 @@
/** @file
*****************************************************************************
Declaration of interfaces for the class PourOutput.
*****************************************************************************
* @author This file is part of libzerocash, developed by the Zerocash
* project and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef POUROUTPUT_H_
#define POUROUTPUT_H_
#include "Coin.h"
#include "ZerocashParams.h"
namespace libzerocash {
class PourOutput {
public:
PourOutput(uint64_t val);
PourOutput(const Coin new_coin,
const PublicAddress to_address);
Coin new_coin;
PublicAddress to_address;
};
} /* namespace libzerocash */
#endif /* POUROUTPUT_H_ */

View File

@ -1,12 +0,0 @@
/** @file
*****************************************************************************
Implementation of interfaces for the class PourProver.
*****************************************************************************
* @author This file is part of libzerocash, developed by the Zerocash
* project and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#include "PourProver.h"

View File

@ -1,65 +0,0 @@
/** @file
*****************************************************************************
Declaration of interfaces for the class PourProver.
*****************************************************************************
* @author This file is part of libzerocash, developed by the Zerocash
* project and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef POURPROVER_H_
#define POURPROVER_H_
#include "ZerocashParams.h"
#include "boost/array.hpp"
#include "PourTransaction.h"
#include "CoinCommitment.h"
namespace libzerocash {
class PourProver {
public:
static bool VerifyProof(
ZerocashParams& params,
const std::vector<unsigned char>& pubkeyHash,
const std::vector<unsigned char>& rt,
const uint64_t vpub_old,
const uint64_t vpub_new,
const boost::array<std::vector<unsigned char>, 2> serials,
const boost::array<std::vector<unsigned char>, 2> commitments,
const boost::array<std::vector<unsigned char>, 2> macs,
const std::string &zkSNARK
) {
PourTransaction pourtx;
pourtx.version = 1;
pourtx.publicOldValue.resize(8);
pourtx.publicNewValue.resize(8);
convertIntToBytesVector(vpub_old, pourtx.publicOldValue);
convertIntToBytesVector(vpub_new, pourtx.publicNewValue);
pourtx.serialNumber_1 = serials[0];
pourtx.serialNumber_2 = serials[1];
{
CoinCommitment cm;
cm.commitmentValue = commitments[0];
pourtx.cm_1 = cm;
}
{
CoinCommitment cm;
cm.commitmentValue = commitments[1];
pourtx.cm_2 = cm;
}
pourtx.MAC_1 = macs[0];
pourtx.MAC_2 = macs[1];
pourtx.zkSNARK = zkSNARK;
return pourtx.verify(params, pubkeyHash, rt);
}
};
}
#endif /* POURPROVER_H_ */

View File

@ -1,443 +0,0 @@
/** @file
*****************************************************************************
Implementation of interfaces for the class PourTransaction.
See PourTransaction.h .
*****************************************************************************
* @author This file is part of libzerocash, developed by the Zerocash
* project and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#include "Zerocash.h"
#include "PourTransaction.h"
#include "PourInput.h"
#include "PourOutput.h"
#include "libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp"
#include "zerocash_pour_gadget.hpp"
#include "zerocash_pour_ppzksnark.hpp"
namespace libzerocash {
PourTransaction::PourTransaction(): cm_1(), cm_2() {
}
PourTransaction::PourTransaction(ZerocashParams& params,
const std::vector<unsigned char>& pubkeyHash,
const MerkleRootType& rt,
std::vector<PourInput> inputs,
std::vector<PourOutput> outputs,
uint64_t vpub_old,
uint64_t vpub_new
) :
publicOldValue(ZC_V_SIZE), publicNewValue(ZC_V_SIZE), serialNumber_1(ZC_SN_SIZE), serialNumber_2(ZC_SN_SIZE), MAC_1(ZC_H_SIZE), MAC_2(ZC_H_SIZE)
{
if (inputs.size() > 2 || outputs.size() > 2) {
throw std::length_error("Too many inputs or outputs specified");
}
while (inputs.size() < 2) {
// Push a dummy input of value 0.
inputs.push_back(PourInput(params.getTreeDepth()));
}
while (outputs.size() < 2) {
// Push a dummy output of value 0.
outputs.push_back(PourOutput(0));
}
init(1,
params,
rt,
inputs[0].old_coin,
inputs[1].old_coin,
inputs[0].old_address,
inputs[1].old_address,
inputs[0].merkle_index,
inputs[1].merkle_index,
inputs[0].path,
inputs[1].path,
outputs[0].to_address,
outputs[1].to_address,
vpub_old,
vpub_new,
pubkeyHash,
outputs[0].new_coin,
outputs[1].new_coin);
}
PourTransaction::PourTransaction(uint16_t version_num,
ZerocashParams& params,
const MerkleRootType& rt,
const Coin& c_1_old,
const Coin& c_2_old,
const Address& addr_1_old,
const Address& addr_2_old,
const size_t patMerkleIdx_1,
const size_t patMerkleIdx_2,
const merkle_authentication_path& patMAC_1,
const merkle_authentication_path& patMAC_2,
const PublicAddress& addr_1_new,
const PublicAddress& addr_2_new,
uint64_t v_pub_old,
uint64_t v_pub_new,
const std::vector<unsigned char>& pubkeyHash,
const Coin& c_1_new,
const Coin& c_2_new) :
publicOldValue(ZC_V_SIZE), publicNewValue(ZC_V_SIZE), serialNumber_1(ZC_SN_SIZE), serialNumber_2(ZC_SN_SIZE), MAC_1(ZC_H_SIZE), MAC_2(ZC_H_SIZE)
{
init(version_num, params, rt, c_1_old, c_2_old, addr_1_old, addr_2_old, patMerkleIdx_1, patMerkleIdx_2,
patMAC_1, patMAC_2, addr_1_new, addr_2_new, v_pub_old, v_pub_new, pubkeyHash, c_1_new, c_2_new);
}
std::vector<unsigned char> from_uint256(const uint256 &in)
{
return std::vector<unsigned char>(in.begin(), in.end());
}
void PourTransaction::init(uint16_t version_num,
ZerocashParams& params,
const MerkleRootType& rt,
const Coin& c_1_old,
const Coin& c_2_old,
const Address& addr_1_old,
const Address& addr_2_old,
const size_t patMerkleIdx_1,
const size_t patMerkleIdx_2,
const merkle_authentication_path& patMAC_1,
const merkle_authentication_path& patMAC_2,
const PublicAddress& addr_1_new,
const PublicAddress& addr_2_new,
uint64_t v_pub_old,
uint64_t v_pub_new,
const std::vector<unsigned char>& pubkeyHash,
const Coin& c_1_new,
const Coin& c_2_new)
{
params.loadProvingKey();
this->version = version_num;
convertIntToBytesVector(v_pub_old, this->publicOldValue);
convertIntToBytesVector(v_pub_new, this->publicNewValue);
this->cm_1 = c_1_new.getCoinCommitment();
this->cm_2 = c_2_new.getCoinCommitment();
std::vector<bool> root_bv(ZC_ROOT_SIZE * 8);
std::vector<bool> addr_pk_new_1_bv(ZC_A_PK_SIZE * 8);
std::vector<bool> addr_pk_new_2_bv(ZC_A_PK_SIZE * 8);
std::vector<bool> addr_sk_old_1_bv(ZC_A_SK_SIZE * 8);
std::vector<bool> addr_sk_old_2_bv(ZC_A_SK_SIZE * 8);
std::vector<bool> rand_new_1_bv(ZC_R_SIZE * 8);
std::vector<bool> rand_new_2_bv(ZC_R_SIZE * 8);
std::vector<bool> rand_old_1_bv(ZC_R_SIZE * 8);
std::vector<bool> rand_old_2_bv(ZC_R_SIZE * 8);
std::vector<bool> nonce_new_1_bv(ZC_RHO_SIZE * 8);
std::vector<bool> nonce_new_2_bv(ZC_RHO_SIZE * 8);
std::vector<bool> nonce_old_1_bv(ZC_RHO_SIZE * 8);
std::vector<bool> nonce_old_2_bv(ZC_RHO_SIZE * 8);
std::vector<bool> val_new_1_bv(ZC_V_SIZE * 8);
std::vector<bool> val_new_2_bv(ZC_V_SIZE * 8);
std::vector<bool> val_old_pub_bv(ZC_V_SIZE * 8);
std::vector<bool> val_new_pub_bv(ZC_V_SIZE * 8);
std::vector<bool> val_old_1_bv(ZC_V_SIZE * 8);
std::vector<bool> val_old_2_bv(ZC_V_SIZE * 8);
std::vector<bool> cm_new_1_bv(ZC_CM_SIZE * 8);
std::vector<bool> cm_new_2_bv(ZC_CM_SIZE * 8);
convertBytesVectorToVector(rt, root_bv);
convertBytesVectorToVector(c_1_new.getCoinCommitment().getCommitmentValue(), cm_new_1_bv);
convertBytesVectorToVector(c_2_new.getCoinCommitment().getCommitmentValue(), cm_new_2_bv);
{
auto a = from_uint256(addr_1_old.getPrivateAddress().getAddressSecret());
auto b = from_uint256(addr_2_old.getPrivateAddress().getAddressSecret());
convertBytesVectorToVector(a, addr_sk_old_1_bv);
convertBytesVectorToVector(b, addr_sk_old_2_bv);
}
{
auto a = from_uint256(addr_1_new.getPublicAddressSecret());
auto b = from_uint256(addr_2_new.getPublicAddressSecret());
convertBytesVectorToVector(a, addr_pk_new_1_bv);
convertBytesVectorToVector(b, addr_pk_new_2_bv);
}
convertBytesVectorToVector(c_1_old.getR(), rand_old_1_bv);
convertBytesVectorToVector(c_2_old.getR(), rand_old_2_bv);
convertBytesVectorToVector(c_1_new.getR(), rand_new_1_bv);
convertBytesVectorToVector(c_2_new.getR(), rand_new_2_bv);
convertBytesVectorToVector(c_1_old.getRho(), nonce_old_1_bv);
convertBytesVectorToVector(c_2_old.getRho(), nonce_old_2_bv);
convertBytesVectorToVector(c_1_new.getRho(), nonce_new_1_bv);
convertBytesVectorToVector(c_2_new.getRho(), nonce_new_2_bv);
std::vector<unsigned char> v_old_1_conv(ZC_V_SIZE, 0);
convertIntToBytesVector(c_1_old.getValue(), v_old_1_conv);
libzerocash::convertBytesVectorToVector(v_old_1_conv, val_old_1_bv);
std::vector<unsigned char> v_old_2_conv(ZC_V_SIZE, 0);
convertIntToBytesVector(c_2_old.getValue(), v_old_2_conv);
libzerocash::convertBytesVectorToVector(v_old_2_conv, val_old_2_bv);
std::vector<unsigned char> v_new_1_conv(ZC_V_SIZE, 0);
convertIntToBytesVector(c_1_new.getValue(), v_new_1_conv);
libzerocash::convertBytesVectorToVector(v_new_1_conv, val_new_1_bv);
std::vector<unsigned char> v_new_2_conv(ZC_V_SIZE, 0);
convertIntToBytesVector(c_2_new.getValue(), v_new_2_conv);
libzerocash::convertBytesVectorToVector(v_new_2_conv, val_new_2_bv);
convertBytesVectorToVector(this->publicOldValue, val_old_pub_bv);
convertBytesVectorToVector(this->publicNewValue, val_new_pub_bv);
std::vector<bool> nonce_old_1(ZC_RHO_SIZE * 8);
copy(nonce_old_1_bv.begin(), nonce_old_1_bv.end(), nonce_old_1.begin());
nonce_old_1.erase(nonce_old_1.end()-2, nonce_old_1.end());
nonce_old_1.insert(nonce_old_1.begin(), 1);
nonce_old_1.insert(nonce_old_1.begin(), 0);
std::vector<bool> sn_internal_1;
concatenateVectors(addr_sk_old_1_bv, nonce_old_1, sn_internal_1);
std::vector<bool> sn_old_1_bv(ZC_SN_SIZE * 8);
hashVector(sn_internal_1, sn_old_1_bv);
convertVectorToBytesVector(sn_old_1_bv, this->serialNumber_1);
std::vector<bool> nonce_old_2(ZC_RHO_SIZE * 8);
copy(nonce_old_2_bv.begin(), nonce_old_2_bv.end(), nonce_old_2.begin());
nonce_old_2.erase(nonce_old_2.end()-2, nonce_old_2.end());
nonce_old_2.insert(nonce_old_2.begin(), 1);
nonce_old_2.insert(nonce_old_2.begin(), 0);
std::vector<bool> sn_internal_2;
concatenateVectors(addr_sk_old_2_bv, nonce_old_2, sn_internal_2);
std::vector<bool> sn_old_2_bv(ZC_SN_SIZE * 8);
hashVector(sn_internal_2, sn_old_2_bv);
convertVectorToBytesVector(sn_old_2_bv, this->serialNumber_2);
unsigned char h_S_bytes[ZC_H_SIZE];
unsigned char pubkeyHash_bytes[ZC_H_SIZE];
convertBytesVectorToBytes(pubkeyHash, pubkeyHash_bytes);
SHA256_CTX sha256;
SHA256_Init(&sha256);
SHA256_Update(&sha256, pubkeyHash_bytes, ZC_H_SIZE);
SHA256_Final(h_S_bytes, &sha256);
std::vector<bool> h_S_bv(ZC_H_SIZE * 8);
convertBytesToVector(h_S_bytes, h_S_bv);
std::vector<bool> h_S_internal1(ZC_H_SIZE * 8);
convertBytesToVector(h_S_bytes, h_S_internal1);
h_S_internal1.erase(h_S_internal1.end()-3, h_S_internal1.end());
std::vector<bool> h_S_internal2 = h_S_internal1;
h_S_internal1.insert(h_S_internal1.begin(), 0);
h_S_internal1.insert(h_S_internal1.begin(), 0);
h_S_internal1.insert(h_S_internal1.begin(), 1);
h_S_internal2.insert(h_S_internal2.begin(), 1);
h_S_internal2.insert(h_S_internal2.begin(), 0);
h_S_internal2.insert(h_S_internal2.begin(), 1);
std::vector<bool> MAC_1_internal;
concatenateVectors(addr_sk_old_1_bv, h_S_internal1, MAC_1_internal);
std::vector<bool> MAC_1_bv(ZC_H_SIZE * 8);
hashVector(MAC_1_internal, MAC_1_bv);
convertVectorToBytesVector(MAC_1_bv, this->MAC_1);
std::vector<bool> MAC_2_internal;
concatenateVectors(addr_sk_old_2_bv, h_S_internal2, MAC_2_internal);
std::vector<bool> MAC_2_bv(ZC_H_SIZE * 8);
hashVector(MAC_2_internal, MAC_2_bv);
convertVectorToBytesVector(MAC_2_bv, this->MAC_2);
if(this->version > 0){
auto proofObj = zerocash_pour_ppzksnark_prover<ZerocashParams::zerocash_pp>(params.getProvingKey(),
{ patMAC_1, patMAC_2 },
{ patMerkleIdx_1, patMerkleIdx_2 },
root_bv,
{ addr_pk_new_1_bv, addr_pk_new_2_bv },
{ addr_sk_old_1_bv, addr_sk_old_2_bv },
{ rand_new_1_bv, rand_new_2_bv },
{ rand_old_1_bv, rand_old_2_bv },
{ nonce_new_1_bv, nonce_new_2_bv },
{ nonce_old_1_bv, nonce_old_2_bv },
{ val_new_1_bv, val_new_2_bv },
val_old_pub_bv,
val_new_pub_bv,
{ val_old_1_bv, val_old_2_bv },
h_S_bv);
std::stringstream ss;
ss << proofObj;
this->zkSNARK = ss.str();
} else {
this->zkSNARK = std::string(1235,'A');
}
// TODO: when h_Sig is constructed properly as per spec
// replace uint256() with it
ZCNoteEncryption encryptor = ZCNoteEncryption(uint256());
{
std::vector<unsigned char> plaintext_internals;
plaintext_internals.insert(plaintext_internals.end(), c_1_new.coinValue.begin(), c_1_new.coinValue.end());
plaintext_internals.insert(plaintext_internals.end(), c_1_new.r.begin(), c_1_new.r.end());
plaintext_internals.insert(plaintext_internals.end(), c_1_new.rho.begin(), c_1_new.rho.end());
std::vector<unsigned char> memo(ZC_MEMO_SIZE, 0x00);
plaintext_internals.insert(plaintext_internals.end(), memo.begin(), memo.end());
assert(plaintext_internals.size() == 216);
boost::array<unsigned char, 216> pt;
memcpy(&pt[0], &plaintext_internals[0], 216);
this->ciphertext_1 = encryptor.encrypt(addr_1_new.getEncryptionPublicKey(),
pt);
}
{
std::vector<unsigned char> plaintext_internals;
plaintext_internals.insert(plaintext_internals.end(), c_2_new.coinValue.begin(), c_2_new.coinValue.end());
plaintext_internals.insert(plaintext_internals.end(), c_2_new.r.begin(), c_2_new.r.end());
plaintext_internals.insert(plaintext_internals.end(), c_2_new.rho.begin(), c_2_new.rho.end());
std::vector<unsigned char> memo(ZC_MEMO_SIZE, 0x00);
plaintext_internals.insert(plaintext_internals.end(), memo.begin(), memo.end());
assert(plaintext_internals.size() == 216);
boost::array<unsigned char, 216> pt;
memcpy(&pt[0], &plaintext_internals[0], 216);
this->ciphertext_2 = encryptor.encrypt(addr_2_new.getEncryptionPublicKey(),
pt);
}
this->ephemeralKey = encryptor.get_epk();
}
bool PourTransaction::verify(ZerocashParams& params,
const std::vector<unsigned char> &pubkeyHash,
const MerkleRootType &merkleRoot) const
{
if(this->version == 0){
return true;
}
zerocash_pour_proof<ZerocashParams::zerocash_pp> proof_SNARK;
std::stringstream ss;
ss.str(this->zkSNARK);
ss >> proof_SNARK;
if (merkleRoot.size() != ZC_ROOT_SIZE) { return false; }
if (pubkeyHash.size() != ZC_H_SIZE) { return false; }
if (this->serialNumber_1.size() != ZC_SN_SIZE) { return false; }
if (this->serialNumber_2.size() != ZC_SN_SIZE) { return false; }
if (this->publicOldValue.size() != ZC_V_SIZE) { return false; }
if (this->publicNewValue.size() != ZC_V_SIZE) { return false; }
if (this->MAC_1.size() != ZC_H_SIZE) { return false; }
if (this->MAC_2.size() != ZC_H_SIZE) { return false; }
std::vector<bool> root_bv(ZC_ROOT_SIZE * 8);
std::vector<bool> sn_old_1_bv(ZC_SN_SIZE * 8);
std::vector<bool> sn_old_2_bv(ZC_SN_SIZE * 8);
std::vector<bool> cm_new_1_bv(ZC_CM_SIZE * 8);
std::vector<bool> cm_new_2_bv(ZC_CM_SIZE * 8);
std::vector<bool> val_old_pub_bv(ZC_V_SIZE * 8);
std::vector<bool> val_new_pub_bv(ZC_V_SIZE * 8);
std::vector<bool> MAC_1_bv(ZC_H_SIZE * 8);
std::vector<bool> MAC_2_bv(ZC_H_SIZE * 8);
convertBytesVectorToVector(merkleRoot, root_bv);
convertBytesVectorToVector(this->serialNumber_1, sn_old_1_bv);
convertBytesVectorToVector(this->serialNumber_2, sn_old_2_bv);
convertBytesVectorToVector(this->cm_1.getCommitmentValue(), cm_new_1_bv);
convertBytesVectorToVector(this->cm_2.getCommitmentValue(), cm_new_2_bv);
convertBytesVectorToVector(this->publicOldValue, val_old_pub_bv);
convertBytesVectorToVector(this->publicNewValue, val_new_pub_bv);
convertBytesVectorToVector(this->MAC_1, MAC_1_bv);
convertBytesVectorToVector(this->MAC_2, MAC_2_bv);
unsigned char h_S_bytes[ZC_H_SIZE];
unsigned char pubkeyHash_bytes[ZC_H_SIZE];
convertBytesVectorToBytes(pubkeyHash, pubkeyHash_bytes);
SHA256_CTX sha256;
SHA256_Init(&sha256);
SHA256_Update(&sha256, pubkeyHash_bytes, ZC_H_SIZE);
SHA256_Final(h_S_bytes, &sha256);
std::vector<bool> h_S_internal(ZC_H_SIZE * 8);
convertBytesToVector(h_S_bytes, h_S_internal);
h_S_internal.erase(h_S_internal.end()-2, h_S_internal.end());
h_S_internal.insert(h_S_internal.begin(), 0);
h_S_internal.insert(h_S_internal.begin(), 1);
std::vector<bool> h_S_bv(ZC_H_SIZE * 8);
convertBytesToVector(h_S_bytes, h_S_bv);
bool snark_result = zerocash_pour_ppzksnark_verifier<ZerocashParams::zerocash_pp>(params.getVerificationKey(),
root_bv,
{ sn_old_1_bv, sn_old_2_bv },
{ cm_new_1_bv, cm_new_2_bv },
val_old_pub_bv,
val_new_pub_bv,
h_S_bv,
{ MAC_1_bv, MAC_2_bv },
proof_SNARK);
return snark_result;
}
const std::vector<unsigned char>& PourTransaction::getSpentSerial1() const{
return this->serialNumber_1;
}
const std::vector<unsigned char>& PourTransaction::getSpentSerial2() const{
return this->serialNumber_2;
}
const ZCNoteEncryption::Ciphertext& PourTransaction::getCiphertext1() const {
return this->ciphertext_1;
}
const ZCNoteEncryption::Ciphertext& PourTransaction::getCiphertext2() const {
return this->ciphertext_2;
}
/**
* Returns the hash of the first new coin commitment output by this Pour.
*/
const CoinCommitmentValue& PourTransaction::getNewCoinCommitmentValue1() const{
return this->cm_1.getCommitmentValue();
}
/**
* Returns the hash of the second new coin commitment output by this Pour.
*/
const CoinCommitmentValue& PourTransaction::getNewCoinCommitmentValue2() const{
return this->cm_2.getCommitmentValue();
}
uint64_t PourTransaction::getPublicValueIn() const{
return convertBytesVectorToInt(this->publicOldValue);
}
uint64_t PourTransaction::getPublicValueOut() const{
return convertBytesVectorToInt(this->publicNewValue);
}
} /* namespace libzerocash */

View File

@ -1,204 +0,0 @@
/** @file
*****************************************************************************
Declaration of interfaces for the class PourTransaction.
*****************************************************************************
* @author This file is part of libzerocash, developed by the Zerocash
* project and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef POURTRANSACTION_H_
#define POURTRANSACTION_H_
#include "Coin.h"
#include "ZerocashParams.h"
#include "Zerocash.h"
#include "PourInput.h"
#include "PourOutput.h"
#include <stdexcept>
#include <bitset>
#include <boost/array.hpp>
#include "uint256.h"
#include "zcash/NoteEncryption.hpp"
typedef std::vector<unsigned char> CoinCommitmentValue;
namespace libzerocash {
/***************************** Pour transaction ******************************/
class PourTransaction {
friend class PourProver;
public:
PourTransaction();
PourTransaction(ZerocashParams& params,
const std::vector<unsigned char>& pubkeyHash,
const MerkleRootType& rt,
const std::vector<PourInput> inputs,
const std::vector<PourOutput> outputs,
uint64_t vpub_old,
uint64_t vpub_new
);
/**
* Generates a transaction pouring the funds in two existing coins into two new coins and optionally
* converting some of those funds back into the base currency.
*
*
* @param version_num the version of the transaction to create
* @param params the cryptographic parameters used to generate the proofs.
* @param root the root of the merkle tree containing the two existing coins
* @param c_1_old the first existing coin
* @param c_2_old the second existing coin
* @param addr_1_old the address the first coin was paid to
* @param addr_2_old the address the second coin was paid to
* @param path_1 path showing that first coin is in the merkle tree rooted at root
* @param path_2 path showing that the second coin is n the merkle tree rooted at root
* @param addr_1_new the public address to pay the first new coin to
* @param addr_2_new the public address to pay the second new coin to
* @param v_pub the amount of funds to convert back to the base currency
* @param pubkeyHash the hash of a public key to bind into the transaction
* @param c_1_new the first of the new coins the funds are being poured into
* @param c_2_new the second of the new coins the funds are being poured into
*/
PourTransaction(uint16_t version_num,
ZerocashParams& params,
const MerkleRootType& roott,
const Coin& c_1_old,
const Coin& c_2_old,
const Address& addr_1_old,
const Address& addr_2_old,
const size_t patMerkleIdx_1,
const size_t patMerkleIdx_2,
const merkle_authentication_path& path_1,
const merkle_authentication_path& path_2,
const PublicAddress& addr_1_new,
const PublicAddress& addr_2_new,
uint64_t v_pub_in,
uint64_t v_pub_out,
const std::vector<unsigned char>& pubkeyHash,
const Coin& c_1_new,
const Coin& c_2_new);
void init(uint16_t version_num,
ZerocashParams& params,
const MerkleRootType& roott,
const Coin& c_1_old,
const Coin& c_2_old,
const Address& addr_1_old,
const Address& addr_2_old,
const size_t patMerkleIdx_1,
const size_t patMerkleIdx_2,
const merkle_authentication_path& path_1,
const merkle_authentication_path& path_2,
const PublicAddress& addr_1_new,
const PublicAddress& addr_2_new,
uint64_t v_pub_in,
uint64_t v_pub_out,
const std::vector<unsigned char>& pubkeyHash,
const Coin& c_1_new,
const Coin& c_2_new);
/**
* Verifies the pour transaction.
*
* @param params the cryptographic parameters used to verify the proofs.
* @param pubkeyHash the hash of a public key that we verify is bound to the transaction
* @param merkleRoot the root of the merkle tree the coins were in.
* @return ture if correct, false otherwise.
*/
bool verify(ZerocashParams& params,
const std::vector<unsigned char> &pubkeyHash,
const MerkleRootType &merkleRoot) const;
const std::vector<unsigned char>& getSpentSerial1() const;
const std::vector<unsigned char>& getSpentSerial2() const;
const ZCNoteEncryption::Ciphertext& getCiphertext1() const;
const ZCNoteEncryption::Ciphertext& getCiphertext2() const;
/**
* Returns the hash of the first new coin generated by this Pour.
*
* @return the coin hash
*/
const CoinCommitmentValue& getNewCoinCommitmentValue1() const;
/**
* Returns the hash of the second new coin generated by this Pour.
*
* @return the coin hash
*/
const CoinCommitmentValue& getNewCoinCommitmentValue2() const;
uint64_t getPublicValueIn() const;
uint64_t getPublicValueOut() const;
std::string unpack(boost::array<std::vector<unsigned char>, 2>& serials,
boost::array<std::vector<unsigned char>, 2>& commitments,
boost::array<std::vector<unsigned char>, 2>& macs,
boost::array<ZCNoteEncryption::Ciphertext, 2>& ciphertexts,
uint256& epk
) const {
serials[0] = this->serialNumber_1;
serials[1] = this->serialNumber_2;
commitments[0] = this->cm_1.getCommitmentValue();
commitments[1] = this->cm_2.getCommitmentValue();
macs[0] = this->MAC_1;
macs[1] = this->MAC_2;
ciphertexts[0] = this->ciphertext_1;
ciphertexts[1] = this->ciphertext_2;
epk = this->ephemeralKey;
return this->zkSNARK;
}
// just hashes a few fields to see if integrity is correct.
// useful for debugging since there's such bad error handling
// currently
void debug_print() {
#define DEBUG_PRINT_POUR_FIELD(X, NAME) {\
std::hash<std::string> h; \
std::cout << NAME << ": " << h(std::string(X.begin(), X.end())) << std::endl;\
}
DEBUG_PRINT_POUR_FIELD(publicOldValue, "publicOldValue");
DEBUG_PRINT_POUR_FIELD(publicNewValue, "publicNewValue");
DEBUG_PRINT_POUR_FIELD(serialNumber_1, "serialNumber_1");
DEBUG_PRINT_POUR_FIELD(serialNumber_2, "serialNumber_2");
{
auto v = cm_1.getCommitmentValue();
DEBUG_PRINT_POUR_FIELD(v, "cm_1");
}
{
auto v = cm_2.getCommitmentValue();
DEBUG_PRINT_POUR_FIELD(v, "cm_2");
}
DEBUG_PRINT_POUR_FIELD(MAC_1, "MAC_1");
DEBUG_PRINT_POUR_FIELD(MAC_2, "MAC_2");
}
private:
std::vector<unsigned char> publicOldValue; // public input value of the Pour transaction
std::vector<unsigned char> publicNewValue; // public output value of the Pour transaction
std::vector<unsigned char> serialNumber_1; // serial number of input (old) coin #1
std::vector<unsigned char> serialNumber_2; // serial number of input (old) coin #1
CoinCommitment cm_1; // coin commitment for output coin #1
CoinCommitment cm_2; // coin commitment for output coin #2
std::vector<unsigned char> MAC_1; // first MAC (h_1 in paper notation)
std::vector<unsigned char> MAC_2; // second MAC (h_2 in paper notation)
ZCNoteEncryption::Ciphertext ciphertext_1; // ciphertext #1
ZCNoteEncryption::Ciphertext ciphertext_2; // ciphertext #2
uint256 ephemeralKey; // epk
std::string zkSNARK; // contents of the zkSNARK proof itself
uint16_t version; // version for the Pour transaction
};
} /* namespace libzerocash */
#endif /* POURTRANSACTION_H_ */

View File

@ -1,65 +0,0 @@
/** @file
*****************************************************************************
Declaration of exceptions and constants for Zerocash.
*****************************************************************************
* @author This file is part of libzerocash, developed by the Zerocash
* project and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef ZEROCASH_H_
#define ZEROCASH_H_
#include <stdexcept>
#include <vector>
/* The version of this library. */
#define ZEROCASH_VERSION_STRING "0.1"
#define ZEROCASH_VERSION_INT 1
#define ZEROCASH_PROTOCOL_VERSION "1"
#define ZEROCASH_DEFAULT_TREE_SIZE 64
#define ZC_A_PK_SIZE 32
#define ZC_PK_ENC_SIZE 311
#define ZC_SIG_PK_SIZE 32
#define ZC_ADDR_PK_SIZE (ZC_A_PK_SIZE+ZC_PK_ENC_SIZE)
#define ZC_A_SK_SIZE 32
#define ZC_SK_ENC_SIZE 287
#define ZC_ADDR_SK_SIZE (ZC_A_SK_SIZE+ZC_SK_ENC_SIZE)
#define ZC_V_SIZE 8
#define ZC_RHO_SIZE 32
#define ZC_R_SIZE 48
#define ZC_MEMO_SIZE 128
#define ZC_S_SIZE 0
#define ZC_K_SIZE 32
#define ZC_CM_SIZE 32
#define ZC_COIN_SIZE (ZC_ADDR_PK_SIZE+ZC_V_SIZE+ZC_RHO_SIZE+ZC_R_SIZE+ZC_S_SIZE+ZC_CM_SIZE)
#define ZC_TX_MINT_SIZE (ZC_CM_SIZE+ZC_V_SIZE+ZC_K_SIZE+ZC_S_SIZE)
#define ZC_ROOT_SIZE 32
#define ZC_SN_SIZE 32
#define ZC_PK_SIG_SIZE 66
#define ZC_H_SIZE 32
#define ZC_POUR_PROOF_SIZE 288
#define ZC_C_SIZE 173
#define ZC_SIGMA_SIZE 72
#define ZC_TX_POUR_SIZE (ZC_ROOT_SIZE+(2*ZC_SN_SIZE)+(2*ZC_CM_SIZE)+ZC_V_SIZE+ZC_PK_SIG_SIZE+(2*ZC_H_SIZE)+ZC_POUR_PROOF_SIZE+(2*ZC_C_SIZE)+ZC_SIGMA_SIZE)
#define SNARK
typedef std::vector<unsigned char> MerkleRootType;
namespace libsnark {
};
namespace libzerocash {
using namespace libsnark;
};
#include "zerocash/utils/util.h"
#endif /* ZEROCASH_H_ */

View File

@ -1,191 +0,0 @@
/** @file
*****************************************************************************
Implementation of interfaces for the class ZerocashParams.
See ZerocashParams.h .
*****************************************************************************
* @author This file is part of libzerocash, developed by the Zerocash
* project and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#include <fstream>
#include <boost/format.hpp>
#include "Zerocash.h"
#include "ZerocashParams.h"
static void throw_missing_param_file_exception(std::string paramtype, std::string path) {
/* paramtype should be either "proving" or "verifying". */
const char* tmpl = ("Could not open %s key file: %s\n"
"Please refer to user documentation for installing this file.");
throw std::runtime_error((boost::format(tmpl) % paramtype % path).str());
}
namespace libzerocash {
int ZerocashParams::getTreeDepth()
{
return treeDepth;
}
zerocash_pour_keypair<ZerocashParams::zerocash_pp> ZerocashParams::GenerateNewKeyPair(const unsigned int tree_depth)
{
libzerocash::ZerocashParams::zerocash_pp::init_public_params();
libzerocash::zerocash_pour_keypair<libzerocash::ZerocashParams::zerocash_pp> kp_v1 =
libzerocash::zerocash_pour_ppzksnark_generator<libzerocash::ZerocashParams::zerocash_pp>(
libzerocash::ZerocashParams::numPourInputs,
libzerocash::ZerocashParams::numPourOutputs,
tree_depth
);
return kp_v1;
}
void ZerocashParams::SaveProvingKeyToFile(const zerocash_pour_proving_key<ZerocashParams::zerocash_pp>* p_pk_1, std::string path)
{
std::stringstream ssProving;
ssProving << p_pk_1->r1cs_pk;
std::ofstream pkFilePtr;
pkFilePtr.open(path, std::ios::binary);
ssProving.rdbuf()->pubseekpos(0, std::ios_base::out);
pkFilePtr << ssProving.rdbuf();
pkFilePtr.flush();
pkFilePtr.close();
}
void ZerocashParams::SaveVerificationKeyToFile(const zerocash_pour_verification_key<ZerocashParams::zerocash_pp>* p_vk_1, std::string path)
{
std::stringstream ssVerification;
ssVerification << p_vk_1->r1cs_vk;
std::ofstream vkFilePtr;
vkFilePtr.open(path, std::ios::binary);
ssVerification.rdbuf()->pubseekpos(0, std::ios_base::out);
vkFilePtr << ssVerification.rdbuf();
vkFilePtr.flush();
vkFilePtr.close();
}
zerocash_pour_proving_key<ZerocashParams::zerocash_pp> ZerocashParams::LoadProvingKeyFromFile(std::string path, const unsigned int tree_depth)
{
std::stringstream ssProving;
std::ifstream fileProving(path, std::ios::binary);
if(!fileProving.is_open()) {
throw_missing_param_file_exception("proving", path);
}
ssProving << fileProving.rdbuf();
fileProving.close();
ssProving.rdbuf()->pubseekpos(0, std::ios_base::in);
r1cs_ppzksnark_proving_key<ZerocashParams::zerocash_pp> pk_temp;
ssProving >> pk_temp;
return zerocash_pour_proving_key<ZerocashParams::zerocash_pp>(
libzerocash::ZerocashParams::numPourInputs,
libzerocash::ZerocashParams::numPourOutputs,
tree_depth,
std::move(pk_temp)
);
}
zerocash_pour_verification_key<ZerocashParams::zerocash_pp> ZerocashParams::LoadVerificationKeyFromFile(std::string path, const unsigned int tree_depth)
{
std::stringstream ssVerification;
std::ifstream fileVerification(path, std::ios::binary);
if(!fileVerification.is_open()) {
throw_missing_param_file_exception("verification", path);
}
ssVerification << fileVerification.rdbuf();
fileVerification.close();
ssVerification.rdbuf()->pubseekpos(0, std::ios_base::in);
r1cs_ppzksnark_verification_key<ZerocashParams::zerocash_pp> vk_temp;
ssVerification >> vk_temp;
return zerocash_pour_verification_key<ZerocashParams::zerocash_pp>(
libzerocash::ZerocashParams::numPourInputs,
libzerocash::ZerocashParams::numPourOutputs,
std::move(vk_temp)
);
}
ZerocashParams::ZerocashParams(
const unsigned int tree_depth,
zerocash_pour_keypair<ZerocashParams::zerocash_pp> *keypair
) :
treeDepth(tree_depth)
{
params_pk_v1 = new zerocash_pour_proving_key<ZerocashParams::zerocash_pp>(keypair->pk);
params_vk_v1 = new zerocash_pour_verification_key<ZerocashParams::zerocash_pp>(keypair->vk);
}
ZerocashParams::ZerocashParams(
const unsigned int tree_depth,
std::string proving_key_path,
zerocash_pour_verification_key<ZerocashParams::zerocash_pp>* p_vk_1
) :
treeDepth(tree_depth), provingKeyPath(proving_key_path)
{
params_vk_v1 = new zerocash_pour_verification_key<ZerocashParams::zerocash_pp>(*p_vk_1);
params_pk_v1 = NULL;
}
ZerocashParams::ZerocashParams(
const unsigned int tree_depth,
zerocash_pour_proving_key<ZerocashParams::zerocash_pp>* p_pk_1,
zerocash_pour_verification_key<ZerocashParams::zerocash_pp>* p_vk_1
) :
treeDepth(tree_depth)
{
assert(p_pk_1 != NULL || p_vk_1 != NULL);
if (p_pk_1 == NULL) {
params_pk_v1 = NULL;
} else {
params_pk_v1 = new zerocash_pour_proving_key<ZerocashParams::zerocash_pp>(*p_pk_1);
}
if (p_vk_1 == NULL) {
params_vk_v1 = NULL;
} else {
params_vk_v1 = new zerocash_pour_verification_key<ZerocashParams::zerocash_pp>(*p_vk_1);
}
}
ZerocashParams::~ZerocashParams()
{
if (params_pk_v1 != NULL) {
delete params_pk_v1;
}
if (params_vk_v1 != NULL) {
delete params_vk_v1;
}
}
const zerocash_pour_proving_key<ZerocashParams::zerocash_pp>& ZerocashParams::getProvingKey()
{
if (params_pk_v1 != NULL) {
return *params_pk_v1;
} else {
throw std::runtime_error("Pour proving key not set.");
}
}
const zerocash_pour_verification_key<ZerocashParams::zerocash_pp>& ZerocashParams::getVerificationKey()
{
if (params_vk_v1 != NULL) {
return *params_vk_v1;
} else {
throw std::runtime_error("Pour verification key not set.");
}
}
} /* namespace libzerocash */

View File

@ -1,77 +0,0 @@
/** @file
*****************************************************************************
Declaration of interfaces for the class ZerocashParams.
*****************************************************************************
* @author This file is part of libzerocash, developed by the Zerocash
* project and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef PARAMS_H_
#define PARAMS_H_
#include "Zerocash.h"
#include "libsnark/common/default_types/r1cs_ppzksnark_pp.hpp"
#include "libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp"
#include "zerocash_pour_ppzksnark.hpp"
namespace libzerocash {
class ZerocashParams {
public:
typedef default_r1cs_ppzksnark_pp zerocash_pp;
ZerocashParams(
const unsigned int tree_depth,
zerocash_pour_keypair<ZerocashParams::zerocash_pp> *keypair
);
ZerocashParams(
const unsigned int tree_depth,
zerocash_pour_proving_key<ZerocashParams::zerocash_pp>* p_pk_1,
zerocash_pour_verification_key<ZerocashParams::zerocash_pp>* p_vk_1
);
ZerocashParams(
const unsigned int tree_depth,
std::string proving_key_path,
zerocash_pour_verification_key<ZerocashParams::zerocash_pp>* p_vk_1
);
const zerocash_pour_proving_key<zerocash_pp>& getProvingKey();
const zerocash_pour_verification_key<zerocash_pp>& getVerificationKey();
int getTreeDepth();
~ZerocashParams();
static const size_t numPourInputs = 2;
static const size_t numPourOutputs = 2;
static zerocash_pour_keypair<ZerocashParams::zerocash_pp> GenerateNewKeyPair(const unsigned int tree_depth);
static void SaveProvingKeyToFile(const zerocash_pour_proving_key<ZerocashParams::zerocash_pp>* p_pk_1, std::string path);
static void SaveVerificationKeyToFile(const zerocash_pour_verification_key<ZerocashParams::zerocash_pp>* p_vk_1, std::string path);
static zerocash_pour_proving_key<ZerocashParams::zerocash_pp> LoadProvingKeyFromFile(std::string path, const unsigned int tree_depth);
static zerocash_pour_verification_key<ZerocashParams::zerocash_pp> LoadVerificationKeyFromFile(std::string path, const unsigned int tree_depth);
void loadProvingKey()
{
if (params_pk_v1 == NULL) {
std::cout << "loading proving key from path: " << provingKeyPath << std::endl;
params_pk_v1 = new zerocash_pour_proving_key<ZerocashParams::zerocash_pp>(
LoadProvingKeyFromFile(provingKeyPath, treeDepth));
std::cout << "done loading proving key!" << std::endl;
}
}
private:
int treeDepth;
zerocash_pour_proving_key<ZerocashParams::zerocash_pp>* params_pk_v1;
zerocash_pour_verification_key<ZerocashParams::zerocash_pp>* params_vk_v1;
std::string provingKeyPath;
};
} /* namespace libzerocash */
#endif /* PARAMS_H_ */

View File

@ -1,39 +0,0 @@
/** @file
*****************************************************************************
* @author This file is part of libzerocash, developed by the Zerocash
* project and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#include "libsnark/common/default_types/r1cs_ppzksnark_pp.hpp"
#include "libsnark/common/profiling.hpp"
#include "zerocash_pour_ppzksnark/zerocash_pour_gadget.hpp"
using namespace libsnark;
int main(int argc, const char* argv[])
{
start_profiling();
default_r1cs_ppzksnark_pp::init_public_params();
#ifndef DEBUG
printf("this program needs to be compiled with constraint annotations (make debug/make cppdebug)\n");
return 2;
#else
if (argc != 4)
{
printf("usage: %s num_old_coins num_new_coins tree_depth\n", argv[0]);
return 1;
}
int num_old_coins = atoi(argv[1]);
int num_new_coins = atoi(argv[2]);
int tree_depth = atoi(argv[3]);
protoboard<Fr<default_r1cs_ppzksnark_pp> > pb;
libzerocash::zerocash_pour_gadget<Fr<default_r1cs_ppzksnark_pp> > g(pb, num_old_coins, num_new_coins, tree_depth, "pour");
g.generate_r1cs_constraints();
for (auto it : pb.get_constraint_system().constraint_annotations)
{
printf("%s\n", it.second.c_str());
}
#endif
}

View File

@ -1,325 +0,0 @@
/** @file
*****************************************************************************
* @author This file is part of libzerocash, developed by the Zerocash
* project and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#include <algorithm>
#include <random>
#include <set>
#include <vector>
#include "libsnark/common/default_types/r1cs_ppzksnark_pp.hpp"
#include "libsnark/common/data_structures/merkle_tree.hpp"
#include "libsnark/common/utils.hpp"
#include "libsnark/common/profiling.hpp"
#include "libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_gadget.hpp"
#include "zerocash/zerocash_pour_gadget.hpp"
#include "zerocash/zerocash_pour_ppzksnark.hpp"
using namespace libzerocash;
bit_vector int_to_bit_vector(const size_t value, const size_t length)
{
/* the returned result will have 0-th position equal to MSB of
`value`, when written as `length`-bit integer */
assert(log2(value) < length);
bit_vector result(length, false);
for (size_t i = 0; i < length; ++i)
{
result[length-1-i] = ((value >> i) & 1) ? true : false;
}
return result;
}
bit_vector get_random_bit_vector(const size_t length)
{
bit_vector result(length);
std::generate(result.begin(), result.end(), [] { return std::rand() % 2; });
return result;
}
std::vector<size_t> sample_random_positions(const size_t num_positions, const size_t log2_universe_size)
{
/* not asymptotically optimal, but likely not to be a problem in
practice, where num_positions is much smaller than
universe_size */
assert(log2(num_positions) <= log2_universe_size);
assert(log2_universe_size <= 8 * sizeof(size_t));
std::set<size_t> positions;
std::vector<size_t> result;
while (positions.size() != num_positions)
{
size_t new_pos = std::rand();
if (log2_universe_size < 8 * sizeof(size_t))
{
new_pos &= ((1ul << log2_universe_size) - 1); /* clear higher bits appropriately */
}
if (positions.find(new_pos) == positions.end())
{
positions.insert(new_pos);
result.emplace_back(new_pos);
}
}
return result;
}
template<typename FieldT>
bit_vector compute_coin_commitment(const bit_vector &address_public_key,
const bit_vector &coin_value,
const bit_vector &serial_number_nonce,
const bit_vector &address_commitment_nonce)
{
/* commitment_to_address_public_key = H(address_public_key || serial_number_nonce) */
bit_vector commitment_to_address_public_key_hash_input;
commitment_to_address_public_key_hash_input.insert(commitment_to_address_public_key_hash_input.end(),
address_public_key.begin(),
address_public_key.end());
commitment_to_address_public_key_hash_input.insert(commitment_to_address_public_key_hash_input.end(),
serial_number_nonce.begin(),
serial_number_nonce.end());
const bit_vector commitment_to_address_public_key = sha256_two_to_one_hash_gadget<FieldT>::get_hash(commitment_to_address_public_key_hash_input);
/* coin_value_commitment_nonce = H(address_commitment_nonce || commitment_to_address_public_key[0..128]) */
bit_vector coin_value_commitment_nonce_hash_input;
coin_value_commitment_nonce_hash_input.insert(coin_value_commitment_nonce_hash_input.end(),
address_commitment_nonce.begin(),
address_commitment_nonce.end());
coin_value_commitment_nonce_hash_input.insert(coin_value_commitment_nonce_hash_input.end(),
commitment_to_address_public_key.begin(),
commitment_to_address_public_key.begin() + truncated_coin_commitment_length);
const bit_vector coin_value_commitment_nonce = sha256_two_to_one_hash_gadget<FieldT>::get_hash(coin_value_commitment_nonce_hash_input);
/* coin_commitment = H(coin_value_commitment_nonce || 0^{192} || coin_value) */
bit_vector coin_commitment_hash_input;
coin_commitment_hash_input.insert(coin_commitment_hash_input.end(),
coin_value_commitment_nonce.begin(),
coin_value_commitment_nonce.end());
coin_commitment_hash_input.resize(coin_commitment_hash_input.size() + coin_commitment_padding_length, false);
coin_commitment_hash_input.insert(coin_commitment_hash_input.end(),
coin_value.begin(),
coin_value.end());
const bit_vector coin_commitment = sha256_two_to_one_hash_gadget<FieldT>::get_hash(coin_commitment_hash_input);
return coin_commitment;
}
std::vector<size_t> randomly_split_up_value(const size_t value, const size_t num_parts)
{
std::vector<size_t> points(num_parts-1);
std::generate(points.begin(), points.end(), [value] { return std::rand() % value; });
points.emplace_back(0);
points.emplace_back(value);
std::sort(points.begin(), points.end()); /* now points is a num_parts+1-length vector with endpoints of 0 and value */
std::vector<size_t> result(num_parts);
for (size_t i = 0; i < num_parts; ++i)
{
result[i] = points[i+1] - points[i];
}
/* the resulting sum telescopes to value-0 = value */
return result;
}
template<typename ppT>
void test_zerocash_pour_ppzksnark(const size_t num_old_coins, const size_t num_new_coins, const size_t tree_depth)
{
typedef Fr<ppT> FieldT;
assert(log2(num_old_coins) <= tree_depth);
/* information used by the prover in the witness map */
std::vector<merkle_authentication_path> old_coin_authentication_paths(num_old_coins); //
std::vector<size_t> old_coin_merkle_tree_positions; //
bit_vector merkle_tree_root; //
std::vector<bit_vector> new_address_public_keys(num_new_coins); //
std::vector<bit_vector> old_address_secret_keys(num_old_coins); //
std::vector<bit_vector> new_address_commitment_nonces(num_new_coins); //
std::vector<bit_vector> old_address_commitment_nonces(num_old_coins); //
std::vector<bit_vector> new_coin_serial_number_nonces(num_new_coins); //
std::vector<bit_vector> old_coin_serial_number_nonces(num_old_coins); //
std::vector<bit_vector> new_coin_values(num_new_coins); //
bit_vector public_in_value; //
bit_vector public_out_value; //
std::vector<bit_vector> old_coin_values(num_old_coins); //
bit_vector signature_public_key_hash; //
/* generate split for the money */
std::vector<size_t> old_coin_values_as_integers(num_old_coins);
std::generate(old_coin_values_as_integers.begin(), old_coin_values_as_integers.end(), [] { return std::rand() % 10000; });
const size_t total_value_as_integer = std::accumulate(old_coin_values_as_integers.begin(), old_coin_values_as_integers.end(), 0);
std::vector<size_t> all_new_values_as_integers = randomly_split_up_value(total_value_as_integer, num_new_coins + 1);
std::transform(old_coin_values_as_integers.begin(), old_coin_values_as_integers.end(),
old_coin_values.begin(),
[] (const size_t value) { return int_to_bit_vector(value, coin_value_length); });
public_in_value = int_to_bit_vector(0, coin_value_length);
public_out_value = int_to_bit_vector(all_new_values_as_integers[0], coin_value_length);
std::transform(all_new_values_as_integers.begin() + 1, all_new_values_as_integers.end(),
new_coin_values.begin(),
[] (const size_t value) { return int_to_bit_vector(value, coin_value_length); });
/* generate random private values for the prover */
old_coin_merkle_tree_positions = sample_random_positions(num_old_coins, tree_depth);
for (size_t i = 0; i < num_old_coins; ++i)
{
old_address_secret_keys[i] = get_random_bit_vector(address_secret_key_length);
old_address_commitment_nonces[i] = get_random_bit_vector(address_commitment_nonce_length);
old_coin_serial_number_nonces[i] = get_random_bit_vector(serial_number_nonce_length);
}
for (size_t i = 0; i < num_new_coins; ++i)
{
new_address_public_keys[i] = get_random_bit_vector(address_public_key_length);
new_address_commitment_nonces[i] = get_random_bit_vector(address_commitment_nonce_length);
new_coin_serial_number_nonces[i] = get_random_bit_vector(serial_number_nonce_length);
}
merkle_tree<sha256_two_to_one_hash_gadget<FieldT> > tree(tree_depth, coin_commitment_length);
for (size_t i = 0; i < num_old_coins; ++i)
{
/* calculate old coin and place it in the Merkle tree */
/* old_address_public_key = H(old_address_secret_key || 00...) */
bit_vector old_address_public_key_hash_input = old_address_secret_keys[i];
old_address_public_key_hash_input.resize(sha256_block_len);
const bit_vector old_address_public_key = sha256_two_to_one_hash_gadget<FieldT>::get_hash(old_address_public_key_hash_input);
const bit_vector old_coin = compute_coin_commitment<FieldT>(old_address_public_key,
old_coin_values[i],
old_coin_serial_number_nonces[i],
old_address_commitment_nonces[i]);
tree.set_value(old_coin_merkle_tree_positions[i], old_coin);
}
for (size_t i = 0; i < num_old_coins; ++i)
{
/* get the corresponding authentication paths */
old_coin_authentication_paths[i] = tree.get_path(old_coin_merkle_tree_positions[i]);
}
merkle_tree_root = tree.get_root();
signature_public_key_hash = get_random_bit_vector(sha256_digest_len);
/* calculate the values used by the verifier */
std::vector<bit_vector> old_coin_serial_numbers(num_old_coins); //
std::vector<bit_vector> new_coin_commitments(num_new_coins); //
std::vector<bit_vector> signature_public_key_hash_macs(num_old_coins); //
for (size_t i = 0; i < num_old_coins; ++i)
{
/* serial_number = H(address_secret_key || 01 || serial_number_nonce[0..254]) */
bit_vector old_coin_serial_number_hash_input;
old_coin_serial_number_hash_input.insert(old_coin_serial_number_hash_input.end(),
old_address_secret_keys[i].begin(),
old_address_secret_keys[i].end());
old_coin_serial_number_hash_input.push_back(false);
old_coin_serial_number_hash_input.push_back(true);
old_coin_serial_number_hash_input.insert(old_coin_serial_number_hash_input.end(),
old_coin_serial_number_nonces[i].begin(),
old_coin_serial_number_nonces[i].begin() + truncated_serial_number_length);
old_coin_serial_numbers[i] = sha256_two_to_one_hash_gadget<FieldT>::get_hash(old_coin_serial_number_hash_input);
/* signature_public_key_hash_macs[i] = H(old_address_secret_keys[i] || 10 || i || signature_public_key_hash) */
const size_t truncated_signature_public_key_hash_length = indexed_signature_public_key_hash_length - log2(num_old_coins);
bit_vector signature_public_key_hash_macs_hash_input;
signature_public_key_hash_macs_hash_input.insert(signature_public_key_hash_macs_hash_input.end(),
old_address_secret_keys[i].begin(),
old_address_secret_keys[i].end());
signature_public_key_hash_macs_hash_input.push_back(true);
signature_public_key_hash_macs_hash_input.push_back(false);
const bit_vector i_as_bits = int_to_bit_vector(i, log2(num_old_coins));
signature_public_key_hash_macs_hash_input.insert(signature_public_key_hash_macs_hash_input.end(),
i_as_bits.begin(),
i_as_bits.end());
signature_public_key_hash_macs_hash_input.insert(signature_public_key_hash_macs_hash_input.end(),
signature_public_key_hash.begin(),
signature_public_key_hash.begin() + truncated_signature_public_key_hash_length);
signature_public_key_hash_macs[i] = sha256_two_to_one_hash_gadget<FieldT>::get_hash(signature_public_key_hash_macs_hash_input);
}
for (size_t i = 0; i < num_new_coins; ++i)
{
new_coin_commitments[i] = compute_coin_commitment<FieldT>(new_address_public_keys[i],
new_coin_values[i],
new_coin_serial_number_nonces[i],
new_address_commitment_nonces[i]);
}
/* perform basic sanity checks */
{
protoboard<FieldT> pb;
zerocash_pour_gadget<FieldT> pour(pb, num_old_coins, num_new_coins, tree_depth, "pour");
pour.generate_r1cs_constraints();
pour.generate_r1cs_witness(old_coin_authentication_paths,
old_coin_merkle_tree_positions,
merkle_tree_root,
new_address_public_keys,
old_address_secret_keys,
new_address_commitment_nonces,
old_address_commitment_nonces,
new_coin_serial_number_nonces,
old_coin_serial_number_nonces,
new_coin_values,
public_in_value,
public_out_value,
old_coin_values,
signature_public_key_hash);
assert(pb.is_satisfied());
printf("gadget test OK for num_old_coins = %zu, num_new_coins = %zu, tree_depth = %zu\n",
num_old_coins, num_new_coins, tree_depth);
}
/* do the end-to-end test */
zerocash_pour_keypair<ppT> keypair = zerocash_pour_ppzksnark_generator<ppT>(num_old_coins, num_new_coins, tree_depth);
keypair = reserialize<zerocash_pour_keypair<ppT> >(keypair);
zerocash_pour_proof<ppT> proof = zerocash_pour_ppzksnark_prover<ppT>(keypair.pk,
old_coin_authentication_paths,
old_coin_merkle_tree_positions,
merkle_tree_root,
new_address_public_keys,
old_address_secret_keys,
new_address_commitment_nonces,
old_address_commitment_nonces,
new_coin_serial_number_nonces,
old_coin_serial_number_nonces,
new_coin_values,
public_in_value,
public_out_value,
old_coin_values,
signature_public_key_hash);
proof = reserialize<zerocash_pour_proof<ppT> >(proof);
const bool verification_result = zerocash_pour_ppzksnark_verifier<ppT>(keypair.vk,
merkle_tree_root,
old_coin_serial_numbers,
new_coin_commitments,
public_in_value,
public_out_value,
signature_public_key_hash,
signature_public_key_hash_macs,
proof);
printf("Verification result: %s\n", verification_result ? "pass" : "FAIL");
assert(verification_result);
}
int main(int argc, const char * argv[])
{
start_profiling();
default_r1cs_ppzksnark_pp::init_public_params();
test_zerocash_pour_ppzksnark<default_r1cs_ppzksnark_pp>(2, 2, 4);
test_zerocash_pour_ppzksnark<default_r1cs_ppzksnark_pp>(2, 3, 4);
test_zerocash_pour_ppzksnark<default_r1cs_ppzksnark_pp>(3, 2, 4);
test_zerocash_pour_ppzksnark<default_r1cs_ppzksnark_pp>(3, 3, 4);
test_zerocash_pour_ppzksnark<default_r1cs_ppzksnark_pp>(2, 2, 32);
}

View File

@ -1,631 +0,0 @@
/** @file
*****************************************************************************
A test for Zerocash.
*****************************************************************************
* @author This file is part of libzerocash, developed by the Zerocash
* project and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#include <stdlib.h>
#include <iostream>
#define BOOST_TEST_MODULE zerocashTest
#include <boost/test/included/unit_test.hpp>
#include "timer.h"
#include "zerocash/Zerocash.h"
#include "zerocash/ZerocashParams.h"
#include "zerocash/Address.h"
#include "zerocash/CoinCommitment.h"
#include "zerocash/Coin.h"
#include "zerocash/IncrementalMerkleTree.h"
#include "zerocash/MintTransaction.h"
#include "zerocash/PourTransaction.h"
#include "zerocash/PourInput.h"
#include "zerocash/PourOutput.h"
#include "zerocash/utils/util.h"
#include "uint256.h"
using namespace std;
using namespace libsnark;
BOOST_AUTO_TEST_CASE( SaveAndLoadKeysFromFiles ) {
cout << "\nSaveAndLoadKeysFromFiles TEST\n" << endl;
cout << "Creating Params...\n" << endl;
libzerocash::timer_start("Param Generation");
auto keypair = libzerocash::ZerocashParams::GenerateNewKeyPair(INCREMENTAL_MERKLE_TREE_DEPTH);
libzerocash::ZerocashParams p(
INCREMENTAL_MERKLE_TREE_DEPTH,
&keypair
);
libzerocash::timer_stop("Param Generation");
print_mem("after param generation");
cout << "Successfully created Params.\n" << endl;
std::string vk_path = "./zerocashTest-verification-key";
std::string pk_path = "./zerocashTest-proving-key";
libzerocash::timer_start("Saving Proving Key");
libzerocash::ZerocashParams::SaveProvingKeyToFile(
&p.getProvingKey(),
pk_path
);
libzerocash::timer_stop("Saving Proving Key");
libzerocash::timer_start("Saving Verification Key");
libzerocash::ZerocashParams::SaveVerificationKeyToFile(
&p.getVerificationKey(),
vk_path
);
libzerocash::timer_stop("Saving Verification Key");
libzerocash::timer_start("Loading Proving Key");
auto pk_loaded = libzerocash::ZerocashParams::LoadProvingKeyFromFile(pk_path, INCREMENTAL_MERKLE_TREE_DEPTH);
libzerocash::timer_stop("Loading Proving Key");
libzerocash::timer_start("Loading Verification Key");
auto vk_loaded = libzerocash::ZerocashParams::LoadVerificationKeyFromFile(vk_path, INCREMENTAL_MERKLE_TREE_DEPTH);
libzerocash::timer_stop("Loading Verification Key");
cout << "Comparing Proving and Verification key.\n" << endl;
if ( !( p.getProvingKey() == pk_loaded && p.getVerificationKey() == vk_loaded) ) {
BOOST_ERROR("Proving and verification key are not equal.");
}
vector<libzerocash::Coin> coins;
vector<libzerocash::Address> addrs;
cout << "Creating Addresses and Coins...\n" << endl;
for(size_t i = 0; i < 5; i++) {
addrs.push_back(libzerocash::Address::CreateNewRandomAddress());
coins.push_back(libzerocash::Coin(addrs.at(i).getPublicAddress(), i));
}
cout << "Successfully created address and coins.\n" << endl;
cout << "Creating a Mint Transaction...\n" << endl;
libzerocash::MintTransaction minttx(coins.at(0));
cout << "Successfully created a Mint Transaction.\n" << endl;
vector<std::vector<bool>> coinValues(5);
vector<bool> temp_comVal(ZC_CM_SIZE * 8);
for(size_t i = 0; i < coinValues.size(); i++) {
libzerocash::convertBytesVectorToVector(coins.at(i).getCoinCommitment().getCommitmentValue(), temp_comVal);
coinValues.at(i) = temp_comVal;
}
cout << "Creating Merkle Tree...\n" << endl;
libzerocash::IncrementalMerkleTree merkleTree(coinValues, INCREMENTAL_MERKLE_TREE_DEPTH);
cout << "Successfully created Merkle Tree.\n" << endl;
std::vector<bool> index;
cout << "Creating Witness 1...\n" << endl;
merkle_authentication_path witness_1(INCREMENTAL_MERKLE_TREE_DEPTH);
libzerocash::convertIntToVector(1, index);
merkleTree.getWitness(index, witness_1);
cout << "Successfully created Witness 1.\n" << endl;
cout << "Creating Witness 2...\n" << endl;
merkle_authentication_path witness_2(INCREMENTAL_MERKLE_TREE_DEPTH);
libzerocash::convertIntToVector(3, index);
merkleTree.getWitness(index, witness_2);
cout << "Successfully created Witness 2.\n" << endl;
cout << "Creating coins to spend...\n" << endl;
libzerocash::Address newAddress3 = libzerocash::Address::CreateNewRandomAddress();
libzerocash::PublicAddress pubAddress3 = newAddress3.getPublicAddress();
libzerocash::Address newAddress4 = libzerocash::Address::CreateNewRandomAddress();
libzerocash::PublicAddress pubAddress4 = newAddress4.getPublicAddress();
libzerocash::Coin c_1_new(pubAddress3, 2);
libzerocash::Coin c_2_new(pubAddress4, 2);
cout << "Successfully created coins to spend.\n" << endl;
vector<unsigned char> rt(ZC_ROOT_SIZE);
merkleTree.getRootValue(rt);
// XXX: debugging
std::cout << "Root: " << rt.size() << endl;
std::cout << "wit1: " << witness_1.size() << endl;
std::cout << "wit2: " << witness_1.size() << endl;
vector<unsigned char> as(ZC_SIG_PK_SIZE, 'a');
cout << "Creating a pour transaction...\n" << endl;
libzerocash::PourTransaction pourtx(1, p,
rt,
coins.at(1), coins.at(3),
addrs.at(1), addrs.at(3),
1, 3,
witness_1, witness_2,
pubAddress3, pubAddress4,
0,
0,
as,
c_1_new, c_2_new);
cout << "Successfully created a pour transaction.\n" << endl;
std::vector<unsigned char> pubkeyHash(ZC_SIG_PK_SIZE, 'a');
cout << "Verifying a pour transaction...\n" << endl;
bool pourtx_res = pourtx.verify(p, pubkeyHash, rt);
BOOST_CHECK(pourtx_res);
}
BOOST_AUTO_TEST_CASE( PourInputOutputTest ) {
// dummy input
{
libzerocash::PourInput input(INCREMENTAL_MERKLE_TREE_DEPTH);
BOOST_CHECK(input.old_coin.getValue() == 0);
BOOST_CHECK(input.old_address.getPublicAddress() == input.old_coin.getPublicAddress());
}
// dummy output
{
libzerocash::PourOutput output(0);
BOOST_CHECK(output.new_coin.getValue() == 0);
BOOST_CHECK(output.to_address == output.new_coin.getPublicAddress());
}
}
// testing with general situational setup
void test_pour(libzerocash::ZerocashParams& p,
uint64_t vpub_in,
uint64_t vpub_out,
std::vector<uint64_t> inputs, // values of the inputs (max 2)
std::vector<uint64_t> outputs) // values of the outputs (max 2)
{
using pour_input_state = std::tuple<libzerocash::Address, libzerocash::Coin, ZCIncrementalWitness>;
// Construct incremental merkle tree
ZCIncrementalMerkleTree merkleTree;
// Dummy sig_pk
vector<unsigned char> as(ZC_SIG_PK_SIZE, 'a');
vector<libzerocash::PourInput> pour_inputs;
vector<libzerocash::PourOutput> pour_outputs;
vector<pour_input_state> input_state;
for(std::vector<uint64_t>::iterator it = inputs.begin(); it != inputs.end(); ++it) {
libzerocash::Address addr = libzerocash::Address::CreateNewRandomAddress();
libzerocash::Coin coin(addr.getPublicAddress(), *it);
// commitment from coin
uint256 commitment(coin.getCoinCommitment().getCommitmentValue());
// insert commitment into the merkle tree
merkleTree.append(commitment);
// and append to any witnesses
for(vector<pour_input_state>::iterator wit = input_state.begin(); wit != input_state.end(); ++wit) {
std::get<2>(*wit).append(commitment);
}
// store the state temporarily
input_state.push_back(std::make_tuple(addr, coin, merkleTree.witness()));
}
// compute the merkle root we will be working with
auto rt_u = merkleTree.root();
std::vector<unsigned char> rt(rt_u.begin(), rt_u.end());
// get witnesses for all the input coins and construct the pours
for(vector<pour_input_state>::iterator it = input_state.begin(); it != input_state.end(); ++it) {
auto witness = std::get<2>(*it);
auto path = witness.path();
pour_inputs.push_back(libzerocash::PourInput(std::get<1>(*it), std::get<0>(*it), path));
}
// construct dummy outputs with the given values
for(vector<uint64_t>::iterator it = outputs.begin(); it != outputs.end(); ++it) {
pour_outputs.push_back(libzerocash::PourOutput(*it));
}
libzerocash::PourTransaction pourtx(p, as, rt, pour_inputs, pour_outputs, vpub_in, vpub_out);
BOOST_CHECK(pourtx.verify(p, as, rt));
}
BOOST_AUTO_TEST_CASE( PourVpubInTest ) {
auto keypair = libzerocash::ZerocashParams::GenerateNewKeyPair(INCREMENTAL_MERKLE_TREE_DEPTH);
libzerocash::ZerocashParams p(
INCREMENTAL_MERKLE_TREE_DEPTH,
&keypair
);
// Things that should work..
test_pour(p, 0, 0, {1}, {1});
test_pour(p, 0, 0, {2}, {1, 1});
test_pour(p, 0, 0, {2, 2}, {3, 1});
test_pour(p, 0, 1, {1}, {});
test_pour(p, 0, 1, {2}, {1});
test_pour(p, 0, 1, {2, 2}, {2, 1});
test_pour(p, 1, 0, {}, {1});
test_pour(p, 1, 0, {1}, {1, 1});
test_pour(p, 1, 0, {2, 2}, {2, 3});
// Things that should not work...
BOOST_CHECK_THROW(test_pour(p, 0, 1, {1}, {1}), std::invalid_argument);
BOOST_CHECK_THROW(test_pour(p, 0, 1, {2}, {1, 1}), std::invalid_argument);
BOOST_CHECK_THROW(test_pour(p, 0, 1, {2, 2}, {3, 1}), std::invalid_argument);
BOOST_CHECK_THROW(test_pour(p, 0, 2, {1}, {}), std::invalid_argument);
BOOST_CHECK_THROW(test_pour(p, 0, 2, {2}, {1}), std::invalid_argument);
BOOST_CHECK_THROW(test_pour(p, 0, 2, {2, 2}, {2, 1}), std::invalid_argument);
BOOST_CHECK_THROW(test_pour(p, 1, 1, {}, {1}), std::invalid_argument);
BOOST_CHECK_THROW(test_pour(p, 1, 1, {1}, {1, 1}), std::invalid_argument);
BOOST_CHECK_THROW(test_pour(p, 1, 1, {2, 2}, {2, 3}), std::invalid_argument);
BOOST_CHECK_THROW(test_pour(p, 0, 0, {2, 2}, {2, 3}), std::invalid_argument);
}
BOOST_AUTO_TEST_CASE( CoinTest ) {
cout << "\nCOIN TEST\n" << endl;
libzerocash::Address newAddress = libzerocash::Address::CreateNewRandomAddress();
libzerocash::PublicAddress pubAddress = newAddress.getPublicAddress();
libzerocash::Coin coin(pubAddress, 0);
cout << "Successfully created a coin.\n" << endl;
///////////////////////////////////////////////////////////////////////////
libzerocash::timer_start("Coin");
libzerocash::Coin coin2(pubAddress, 0);
libzerocash::timer_stop("Coin");
cout << "Successfully created a coin.\n" << endl;
}
BOOST_AUTO_TEST_CASE( MintTxTest ) {
cout << "\nMINT TRANSACTION TEST\n" << endl;
libzerocash::Address newAddress = libzerocash::Address::CreateNewRandomAddress();
libzerocash::PublicAddress pubAddress = newAddress.getPublicAddress();
vector<unsigned char> value(ZC_V_SIZE, 0);
libzerocash::timer_start("Coin");
const libzerocash::Coin coin(pubAddress, 0);
libzerocash::timer_stop("Coin");
libzerocash::timer_start("Mint Transaction");
libzerocash::MintTransaction minttx(coin);
libzerocash::timer_stop("Mint Transaction");
cout << "Successfully created a mint transaction.\n" << endl;
libzerocash::timer_start("Mint Transaction Verify");
bool minttx_res = minttx.verify();
libzerocash::timer_stop("Mint Transaction Verify");
BOOST_CHECK(minttx_res);
}
BOOST_AUTO_TEST_CASE( PourTxTest ) {
cout << "\nPOUR TRANSACTION TEST\n" << endl;
cout << "Creating Params...\n" << endl;
libzerocash::timer_start("Param Generation");
auto keypair = libzerocash::ZerocashParams::GenerateNewKeyPair(INCREMENTAL_MERKLE_TREE_DEPTH);
libzerocash::ZerocashParams p(
INCREMENTAL_MERKLE_TREE_DEPTH,
&keypair
);
libzerocash::timer_stop("Param Generation");
print_mem("after param generation");
cout << "Successfully created Params.\n" << endl;
vector<libzerocash::Coin> coins;
vector<libzerocash::Address> addrs;
for(size_t i = 0; i < 5; i++) {
addrs.push_back(libzerocash::Address::CreateNewRandomAddress());
coins.push_back(libzerocash::Coin(addrs.at(i).getPublicAddress(), i));
}
cout << "Successfully created coins.\n" << endl;
vector<std::vector<bool>> coinValues(5);
vector<bool> temp_comVal(ZC_CM_SIZE * 8);
for(size_t i = 0; i < coinValues.size(); i++) {
libzerocash::convertBytesVectorToVector(coins.at(i).getCoinCommitment().getCommitmentValue(), temp_comVal);
coinValues.at(i) = temp_comVal;
libzerocash::printVectorAsHex("Coin => ", coinValues.at(i));
}
cout << "Creating Merkle Tree...\n" << endl;
libzerocash::timer_start("Merkle Tree");
libzerocash::IncrementalMerkleTree merkleTree(coinValues, INCREMENTAL_MERKLE_TREE_DEPTH);
libzerocash::timer_stop("Merkle Tree");
cout << "Successfully created Merkle Tree.\n" << endl;
merkle_authentication_path witness_1(INCREMENTAL_MERKLE_TREE_DEPTH);
libzerocash::timer_start("Witness");
std::vector<bool> index;
libzerocash::convertIntToVector(1, index);
if (merkleTree.getWitness(index, witness_1) == false) {
BOOST_ERROR("Could not get witness");
}
libzerocash::timer_stop("Witness");
cout << "Witness 1: " << endl;
for(size_t i = 0; i < witness_1.size(); i++) {
libzerocash::printVectorAsHex(witness_1.at(i));
}
cout << "\n" << endl;
merkle_authentication_path witness_2(INCREMENTAL_MERKLE_TREE_DEPTH);
libzerocash::convertIntToVector(3, index);
if (merkleTree.getWitness(index, witness_2) == false) {
cout << "Could not get witness" << endl;
}
cout << "Witness 2: " << endl;
for(size_t i = 0; i < witness_2.size(); i++) {
libzerocash::printVectorAsHex(witness_2.at(i));
}
cout << "\n" << endl;
libzerocash::Address newAddress3 = libzerocash::Address::CreateNewRandomAddress();
libzerocash::PublicAddress pubAddress3 = newAddress3.getPublicAddress();
libzerocash::Address newAddress4 = libzerocash::Address::CreateNewRandomAddress();
libzerocash::PublicAddress pubAddress4 = newAddress4.getPublicAddress();
libzerocash::Coin c_1_new(pubAddress3, 2);
libzerocash::Coin c_2_new(pubAddress4, 2);
vector<bool> root_bv(ZC_ROOT_SIZE * 8);
merkleTree.getRootValue(root_bv);
vector<unsigned char> rt(ZC_ROOT_SIZE);
libzerocash::convertVectorToBytesVector(root_bv, rt);
vector<unsigned char> ones(ZC_V_SIZE, 1);
vector<unsigned char> twos(ZC_V_SIZE, 2);
vector<unsigned char> as(ZC_SIG_PK_SIZE, 'a');
cout << "Creating a pour transaction...\n" << endl;
libzerocash::timer_start("Pour Transaction");
libzerocash::PourTransaction pourtx(1, p, rt, coins.at(1), coins.at(3), addrs.at(1), addrs.at(3), 1, 3, witness_1, witness_2, pubAddress3, pubAddress4, 0, 0, as, c_1_new, c_2_new);
libzerocash::timer_stop("Pour Transaction");
print_mem("after pour transaction");
cout << "Successfully created a pour transaction.\n" << endl;
std::vector<unsigned char> pubkeyHash(ZC_SIG_PK_SIZE, 'a');
libzerocash::timer_start("Pour Transaction Verify");
bool pourtx_res = pourtx.verify(p, pubkeyHash, rt);
libzerocash::timer_stop("Pour Transaction Verify");
BOOST_CHECK(pourtx_res);
}
BOOST_AUTO_TEST_CASE( MerkleTreeSimpleTest ) {
cout << "\nMERKLE TREE SIMPLE TEST\n" << endl;
vector<libzerocash::Coin> coins;
vector<libzerocash::Address> addrs;
cout << "Creating coins...\n" << endl;
for(size_t i = 0; i < 5; i++) {
addrs.push_back(libzerocash::Address::CreateNewRandomAddress());
coins.push_back(libzerocash::Coin(addrs.at(i).getPublicAddress(), i));
}
cout << "Successfully created coins.\n" << endl;
vector<std::vector<bool>> coinValues(coins.size());
vector<bool> temp_comVal(ZC_CM_SIZE * 8);
for(size_t i = 0; i < coinValues.size(); i++) {
libzerocash::convertBytesVectorToVector(coins.at(i).getCoinCommitment().getCommitmentValue(), temp_comVal);
coinValues.at(i) = temp_comVal;
libzerocash::printVectorAsHex(coinValues.at(i));
}
cout << "Creating Merkle Tree...\n" << endl;
libzerocash::IncrementalMerkleTree merkleTree(64);
vector<bool> root;
merkleTree.getRootValue(root);
cout << "Root: ";
libzerocash::printVectorAsHex(root);
cout << endl;
cout << "Successfully created Merkle Tree.\n" << endl;
cout << "Copying and pruning Merkle Tree...\n" << endl;
libzerocash::IncrementalMerkleTree copyTree = merkleTree;
copyTree.prune();
cout << "Obtaining compact representation and reconstituting tree...\n" << endl;
libzerocash::IncrementalMerkleTreeCompact compactTree = merkleTree.getCompactRepresentation();
cout << "Compact representation vector: ";
libzerocash::printVector(compactTree.getHashList());
libzerocash::IncrementalMerkleTree reconstitutedTree(compactTree);
reconstitutedTree.getRootValue(root);
cout << "New root: ";
libzerocash::printVectorAsHex(root);
cout << endl;
reconstitutedTree.insertVector(coinValues);
merkleTree.insertVector(coinValues);
reconstitutedTree.getRootValue(root);
cout << "New root (added a bunch more): ";
libzerocash::printVectorAsHex(root);
cout << endl;
merkleTree.getRootValue(root);
cout << "Old root (added a bunch more): ";
libzerocash::printVectorAsHex(root);
cout << endl;
merkle_authentication_path witness(16);
std::vector<bool> index;
libzerocash::convertIntToVector(3, index);
if (merkleTree.getWitness(index, witness) == false) {
BOOST_ERROR("Witness generation failed.");
}
cout << "Successfully created witness.\n" << endl;
cout << "Witness: " << endl;
for(size_t i = 0; i < witness.size(); i++) {
libzerocash::printVectorAsHex(witness.at(i));
}
cout << "\n" << endl;
vector<bool> wit1(CSHA256::OUTPUT_SIZE * 8);
vector<bool> wit2(CSHA256::OUTPUT_SIZE * 8);
vector<bool> wit3(CSHA256::OUTPUT_SIZE * 8);
vector<bool> inter_1(CSHA256::OUTPUT_SIZE * 8);
vector<bool> inter_2(CSHA256::OUTPUT_SIZE * 8);
std::vector<bool> zeros(CSHA256::OUTPUT_SIZE * 8, 0);
wit1 = coinValues.at(2);
libzerocash::hashVectors(coinValues.at(0), coinValues.at(1), wit2);
libzerocash::hashVectors(coinValues.at(4), zeros, inter_1);
inter_2 = zeros;
libzerocash::hashVectors(inter_1, inter_2, wit3);
BOOST_CHECK(witness.size() == 64);
for (size_t i = 0; i < 61; i++) {
BOOST_CHECK(witness.at(i) == zeros);
}
BOOST_CHECK(
(witness.at(61) == wit3) &&
(witness.at(62) == wit2) &&
(witness.at(63) == wit1)
);
}
BOOST_AUTO_TEST_CASE( SimpleTxTest ) {
cout << "\nSIMPLE TRANSACTION TEST\n" << endl;
libzerocash::timer_start("Param Generation");
auto keypair = libzerocash::ZerocashParams::GenerateNewKeyPair(INCREMENTAL_MERKLE_TREE_DEPTH);
libzerocash::ZerocashParams p(
INCREMENTAL_MERKLE_TREE_DEPTH,
&keypair
);
libzerocash::timer_stop("Param Generation");
vector<libzerocash::Coin> coins;
vector<libzerocash::Address> addrs;
cout << "Creating Addresses and Coins...\n" << endl;
for(size_t i = 0; i < 5; i++) {
addrs.push_back(libzerocash::Address::CreateNewRandomAddress());
coins.push_back(libzerocash::Coin(addrs.at(i).getPublicAddress(), i));
}
cout << "Successfully created address and coins.\n" << endl;
cout << "Creating a Mint Transaction...\n" << endl;
libzerocash::MintTransaction minttx(coins.at(0));
cout << "Successfully created a Mint Transaction.\n" << endl;
cout << "Verifying a Mint Transaction...\n" << endl;
bool minttx_res = minttx.verify();
vector<std::vector<bool>> coinValues(5);
vector<bool> temp_comVal(ZC_CM_SIZE * 8);
for(size_t i = 0; i < coinValues.size(); i++) {
libzerocash::convertBytesVectorToVector(coins.at(i).getCoinCommitment().getCommitmentValue(), temp_comVal);
coinValues.at(i) = temp_comVal;
}
cout << "Creating Merkle Tree...\n" << endl;
libzerocash::IncrementalMerkleTree merkleTree(coinValues, INCREMENTAL_MERKLE_TREE_DEPTH);
cout << "Successfully created Merkle Tree.\n" << endl;
std::vector<bool> index;
cout << "Creating Witness 1...\n" << endl;
merkle_authentication_path witness_1(INCREMENTAL_MERKLE_TREE_DEPTH);
libzerocash::convertIntToVector(1, index);
if (merkleTree.getWitness(index, witness_1) == false) {
BOOST_ERROR("Could not get witness");
}
cout << "Successfully created Witness 1.\n" << endl;
cout << "Creating Witness 2...\n" << endl;
merkle_authentication_path witness_2(INCREMENTAL_MERKLE_TREE_DEPTH);
libzerocash::convertIntToVector(3, index);
if (merkleTree.getWitness(index, witness_2) == false) {
cout << "Could not get witness" << endl;
}
cout << "Successfully created Witness 2.\n" << endl;
cout << "Creating coins to spend...\n" << endl;
libzerocash::Address newAddress3 = libzerocash::Address::CreateNewRandomAddress();
libzerocash::PublicAddress pubAddress3 = newAddress3.getPublicAddress();
libzerocash::Address newAddress4 = libzerocash::Address::CreateNewRandomAddress();
libzerocash::PublicAddress pubAddress4 = newAddress4.getPublicAddress();
libzerocash::Coin c_1_new(pubAddress3, 2);
libzerocash::Coin c_2_new(pubAddress4, 2);
cout << "Successfully created coins to spend.\n" << endl;
vector<bool> root_bv(ZC_ROOT_SIZE * 8);
merkleTree.getRootValue(root_bv);
vector<unsigned char> rt(ZC_ROOT_SIZE);
libzerocash::convertVectorToBytesVector(root_bv, rt);
vector<unsigned char> as(ZC_SIG_PK_SIZE, 'a');
cout << "Creating a pour transaction...\n" << endl;
libzerocash::PourTransaction pourtx(1, p,
rt,
coins.at(1), coins.at(3),
addrs.at(1), addrs.at(3),
1, 3,
witness_1, witness_2,
pubAddress3, pubAddress4,
0,
0,
as,
c_1_new, c_2_new);
cout << "Successfully created a pour transaction.\n" << endl;
std::vector<unsigned char> pubkeyHash(ZC_SIG_PK_SIZE, 'a');
cout << "Verifying a pour transaction...\n" << endl;
bool pourtx_res = pourtx.verify(p, pubkeyHash, rt);
BOOST_CHECK(minttx_res && pourtx_res);
}

View File

@ -1,188 +0,0 @@
/** @file
*****************************************************************************
Declaration of interfaces for the Zerocash Pour gadget.
The Pour gadget implements the NP statement "Zerocash Pour" described in \[BCGGMTV14].
References:
\[BCGGMTV14]:
"Zerocash: Decentralized Anonymous Payments from Bitcoin",
Eli Ben-Sasson, Alessandro Chiesa, Christina Garman, Matthew Green, Ian Miers, Eran Tromer, Madars Virza,
S&P 2014,
<https://eprint.iacr.org/2014/349>
*****************************************************************************
* @author This file is part of libzerocash, developed by the Zerocash
* project and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef ZEROCASH_POUR_GADGET_HPP_
#define ZEROCASH_POUR_GADGET_HPP_
#include "zerocash_pour_params.hpp"
#include "libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_gadget.hpp"
#include "libsnark/gadgetlib1/gadgets/merkle_tree/merkle_tree_check_read_gadget.hpp"
namespace libzerocash {
using namespace libsnark;
/**
* Gadget for the NP statement Zerocash Pour.
*
* More precisely, this gadgets checks the following NP statement.
* (See Section 4.2 and Section 5.1 of the Zerocash paper for more details.)
*
* (A) old_coin_commitment_variables[i] appears on path old_coin_authentication_paths[i] to merkle_tree_root_variable
*
* (B) old_address_public_keys[i]
* = PRF_{old_address_secret_key_variables[i]}^{addr}(z)
* = H(old_address_secret_key_variables[i] || 00 || z)
* where z = 0...0
*
* (C) old_coin_serial_number_variables[i]
* = PRF_{old_address_secret_key_variables[i]}^{sn}(old_coin_serial_number_nonce_variables[0..254])
* = H(old_address_secret_key_variables[i] || 01 || old_coin_serial_number_nonce_variables[0..254])
*
* Properties (D0) and (D1) jointly ensure that
*
* old_coin_value_commitment_nonces[i]
* = COMM_{old_address_commitment_nonce_variables[i]}(old_address_public_key_variables[i] || old_coin_serial_number_nonce_variables[i])
*
* as follows:
*
* (D0) commitments_to_old_address_public_keys[i]
* = H(old_address_public_key_variables[i] || old_coin_serial_number_nonce_variables[i])
*
* (D1) old_coin_value_commitment_nonces[i]
* = H(old_address_commitment_nonce_variables[i] || commitments_to_old_address_public_keys[i] [0..128])
*
* Given this, (D2) computes old_coin_commitments:
*
* (D2) old_coin_commitment_variables[i]
* = COMM_s(old_coin_value_variables[i] || old_coin_value_commitment_nonces[i])
* = H(old_coin_value_commitment_nonces[i] || 0^{192} || old_coin_value_variables[i])
*
* Here we ignore commitment randomness s, because
* k = old_coin_value_commitment_nonces[i]
* is an output of a statistically-hiding commitment scheme.
*
* While (D0) and (D1) check that old coin commitments are well formed,
* (E0) and (E1) check the same properties for new coins.
*
* (F) mac_of_signature_public_key_hash_variables[i]
* = PRF_{old_address_secret_key_variables[i]}^{pk}(i || signature_public_key_hash_variable)
* = H(old_address_secret_key_variables[i] || 10 || i || signature_public_key_hash_variable)
*
* Here signature_public_key_hash is truncated so that the entire argument fits inside SHA256 block.
* Furthermore, the representation of i is MSB to LSB and is exactly log2(num_old_coins) bits long.
*/
template<typename FieldT>
class zerocash_pour_gadget : public gadget<FieldT> {
public:
size_t tree_depth;
pb_variable_array<FieldT> input_as_field_elements; /* R1CS input */
pb_variable_array<FieldT> input_as_bits; /* unpacked R1CS input */
std::shared_ptr<multipacking_gadget<FieldT> > unpack_inputs;
/* individual components of the unpacked R1CS input */
std::shared_ptr<digest_variable<FieldT> > merkle_tree_root_variable;
std::vector<std::shared_ptr<digest_variable<FieldT> > > old_coin_serial_number_variables;
pb_variable_array<FieldT> old_coin_enforce_commitment;
std::vector<std::shared_ptr<digest_variable<FieldT> > > new_coin_commitment_variables;
pb_variable_array<FieldT> public_old_value_variable;
pb_variable_array<FieldT> public_new_value_variable;
std::shared_ptr<digest_variable<FieldT> > signature_public_key_hash_variable;
std::vector<std::shared_ptr<digest_variable<FieldT> > > mac_of_signature_public_key_hash_variables;
/* TODO */
pb_variable<FieldT> zero;
std::vector<pb_variable_array<FieldT> > new_address_public_key_variables;
std::vector<pb_variable_array<FieldT> > old_address_secret_key_variables;
std::vector<pb_variable_array<FieldT> > new_address_commitment_nonce_variables;
std::vector<pb_variable_array<FieldT> > old_address_commitment_nonce_variables;
std::vector<pb_variable_array<FieldT> > new_coin_serial_number_nonce_variables;
std::vector<pb_variable_array<FieldT> > old_coin_serial_number_nonce_variables;
std::vector<pb_variable_array<FieldT> > new_coin_value_variables;
std::vector<pb_variable_array<FieldT> > old_coin_value_variables;
std::vector<std::shared_ptr<block_variable<FieldT> > > prf_for_old_coin_serial_number_input_variables;
std::vector<std::shared_ptr<sha256_compression_function_gadget<FieldT> > > prfs_for_old_coin_serial_numbers; // (C)
std::vector<std::shared_ptr<digest_variable<FieldT> > > old_address_public_key_variables;
std::vector<std::shared_ptr<block_variable<FieldT> > > prf_for_old_address_public_key_input_variables;
std::vector<std::shared_ptr<sha256_compression_function_gadget<FieldT> > > prfs_for_old_address_public_keys; // (B)
std::vector<std::shared_ptr<digest_variable<FieldT> > > commitments_to_old_address_public_keys;
std::vector<std::shared_ptr<block_variable<FieldT> > > commit_to_old_address_public_key_input_variables;
std::vector<std::shared_ptr<sha256_compression_function_gadget<FieldT> > > commit_to_old_address_public_keys; // (D0)
std::vector<std::shared_ptr<digest_variable<FieldT> > > old_coin_value_commitment_nonces;
std::vector<std::shared_ptr<block_variable<FieldT> > > commit_to_old_coin_value_commitment_nonce_input_variables;
std::vector<std::shared_ptr<sha256_compression_function_gadget<FieldT> > > commit_to_old_coin_value_commitment_nonces; // (D1)
std::vector<std::shared_ptr<digest_variable<FieldT> > > old_coin_commitment_variables;
std::vector<std::shared_ptr<block_variable<FieldT> > > compute_old_coin_commitment_input_variables;
std::vector<std::shared_ptr<sha256_compression_function_gadget<FieldT> > > compute_old_coin_commitments; // (D2)
std::vector<std::shared_ptr<digest_variable<FieldT> > > commitments_to_new_address_public_keys;
std::vector<std::shared_ptr<block_variable<FieldT> > > commit_to_new_address_public_key_input_variables;
std::vector<std::shared_ptr<sha256_compression_function_gadget<FieldT> > > commit_to_new_address_public_keys; // (E0)
std::vector<std::shared_ptr<digest_variable<FieldT> > > new_coin_value_commitment_nonces;
std::vector<std::shared_ptr<block_variable<FieldT> > > commit_to_new_coin_value_commitment_nonce_input_variables;
std::vector<std::shared_ptr<sha256_compression_function_gadget<FieldT> > > commit_to_new_coin_value_commitment_nonces; // (E1)
std::vector<std::shared_ptr<block_variable<FieldT> > > compute_new_coin_commitment_input_variables;
std::vector<std::shared_ptr<sha256_compression_function_gadget<FieldT> > > compute_new_coin_commitments; // (E2)
std::vector<std::shared_ptr<block_variable<FieldT> > > prf_for_macs_of_signature_public_key_hash_input_variables;
std::vector<std::shared_ptr<sha256_compression_function_gadget<FieldT> > > prfs_for_macs_of_signature_public_key_hash; // (F)
std::vector<pb_variable_array<FieldT> > old_coin_merkle_tree_position_variables;
std::vector<std::shared_ptr<merkle_authentication_path_variable<FieldT, sha256_two_to_one_hash_gadget<FieldT> > > > old_coin_authentication_path_variables;
std::vector<std::shared_ptr<merkle_tree_check_read_gadget<FieldT, sha256_two_to_one_hash_gadget<FieldT> > > > old_coin_commitments_in_tree; // (A)
size_t num_old_coins;
size_t num_new_coins;
zerocash_pour_gadget(protoboard<FieldT> &pb, const size_t num_old_coins, const size_t num_new_coins, const size_t tree_depth, const std::string &annotation_prefix);
void generate_r1cs_constraints();
void generate_r1cs_witness(const std::vector<merkle_authentication_path> &old_coin_authentication_paths,
const std::vector<size_t> &old_coin_merkle_tree_positions,
const bit_vector &merkle_tree_root,
const std::vector<bit_vector> &new_address_public_keys,
const std::vector<bit_vector> &old_address_secret_keys,
const std::vector<bit_vector> &new_address_commitment_nonces,
const std::vector<bit_vector> &old_address_commitment_nonces,
const std::vector<bit_vector> &new_coin_serial_number_nonces,
const std::vector<bit_vector> &old_coin_serial_number_nonces,
const std::vector<bit_vector> &new_coin_values,
const bit_vector &public_old_value,
const bit_vector &public_new_value,
const std::vector<bit_vector> &old_coin_values,
const bit_vector &signature_public_key_hash);
};
template<typename FieldT>
r1cs_primary_input<FieldT> zerocash_pour_input_map(const size_t num_old_coins,
const size_t num_new_coins,
const bit_vector &merkle_tree_root,
const std::vector<bit_vector> &old_coin_serial_numbers,
const std::vector<bit_vector> &new_coin_commitments,
const bit_vector &public_old_value,
const bit_vector &public_new_value,
const bit_vector &signature_public_key_hash,
const std::vector<bit_vector> &signature_public_key_hash_macs);
} // libzerocash
#include "zerocash_pour_gadget.tcc"
#endif // ZEROCASH_POUR_GADGET_HPP_

View File

@ -1,503 +0,0 @@
/** @file
*****************************************************************************
Implementation of interfaces for the Zerocash Pour gadget.
See zerocash_pour_gadget.hpp .
*****************************************************************************
* @author This file is part of libzerocash, developed by the Zerocash
* project and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#include "algebra/fields/field_utils.hpp"
namespace libzerocash {
template<typename FieldT>
zerocash_pour_gadget<FieldT>::zerocash_pour_gadget(protoboard<FieldT> &pb,
const size_t num_old_coins,
const size_t num_new_coins,
const size_t tree_depth,
const std::string &annotation_prefix) :
gadget<FieldT>(pb, FMT(annotation_prefix, " zerocash_pour_gadget")),
tree_depth(tree_depth),
num_old_coins(num_old_coins),
num_new_coins(num_new_coins)
{
/* allocate packed inputs */
const size_t input_size_in_bits = sha256_digest_len + num_old_coins*sha256_digest_len + num_new_coins*sha256_digest_len + (coin_value_length * 2) + (num_old_coins + 1) * sha256_digest_len;
const size_t input_size_in_field_elements = div_ceil(input_size_in_bits, FieldT::capacity());
input_as_field_elements.allocate(pb, input_size_in_field_elements, FMT(annotation_prefix, " input_as_field_elements"));
this->pb.set_input_sizes(input_size_in_field_elements);
/* allocate inputs */
merkle_tree_root_variable.reset(new digest_variable<FieldT>(pb, sha256_digest_len, FMT(annotation_prefix, " merkle_tree_root_variable")));
old_coin_enforce_commitment.allocate(pb, num_old_coins, FMT(annotation_prefix, " old_coin_enforce_commitment"));
old_coin_serial_number_variables.resize(num_old_coins);
for (size_t i = 0; i < num_old_coins; ++i)
{
old_coin_serial_number_variables[i].reset(new digest_variable<FieldT>(pb, sha256_digest_len, FMT(annotation_prefix, " old_coin_serial_number_variables_%zu", i)));
}
new_coin_commitment_variables.resize(num_new_coins);
for (size_t i = 0; i < num_new_coins; ++i)
{
new_coin_commitment_variables[i].reset(new digest_variable<FieldT>(pb, sha256_digest_len, FMT(annotation_prefix, " new_coin_commitment_variables_%zu", i)));
}
public_old_value_variable.allocate(pb, coin_value_length, FMT(annotation_prefix, " public_old_value_variable"));
public_new_value_variable.allocate(pb, coin_value_length, FMT(annotation_prefix, " public_new_value_variable"));
signature_public_key_hash_variable.reset(new digest_variable<FieldT>(pb, sha256_digest_len, FMT(annotation_prefix, " signature_public_key_hash")));
mac_of_signature_public_key_hash_variables.resize(num_old_coins);
for (size_t i = 0; i < num_old_coins; ++i)
{
mac_of_signature_public_key_hash_variables[i].reset(new digest_variable<FieldT>(pb, sha256_digest_len, FMT(annotation_prefix, " mac_of_signature_public_key_hash_variables_%zu", i)));
}
/* do the multipacking */
input_as_bits.insert(input_as_bits.end(), merkle_tree_root_variable->bits.begin(), merkle_tree_root_variable->bits.end());
for (size_t i = 0; i < num_old_coins; ++i)
{
input_as_bits.insert(input_as_bits.end(), old_coin_serial_number_variables[i]->bits.begin(), old_coin_serial_number_variables[i]->bits.end());
}
for (size_t i = 0; i < num_new_coins; ++i)
{
input_as_bits.insert(input_as_bits.end(), new_coin_commitment_variables[i]->bits.begin(), new_coin_commitment_variables[i]->bits.end());
}
input_as_bits.insert(input_as_bits.end(), public_old_value_variable.begin(), public_old_value_variable.end());
input_as_bits.insert(input_as_bits.end(), public_new_value_variable.begin(), public_new_value_variable.end());
input_as_bits.insert(input_as_bits.end(), signature_public_key_hash_variable->bits.begin(), signature_public_key_hash_variable->bits.end());
for (size_t i = 0; i < num_old_coins; ++i)
{
input_as_bits.insert(input_as_bits.end(), mac_of_signature_public_key_hash_variables[i]->bits.begin(), mac_of_signature_public_key_hash_variables[i]->bits.end());
}
assert(input_as_bits.size() == input_size_in_bits);
unpack_inputs.reset(new multipacking_gadget<FieldT>(this->pb, input_as_bits, input_as_field_elements, FieldT::capacity(), FMT(this->annotation_prefix, " unpack_inputs")));
pb_linear_combination_array<FieldT> IV = SHA256_default_IV(pb);
zero.allocate(this->pb, FMT(this->annotation_prefix, " zero")); /* TODO */
/* allocate witness */
new_address_public_key_variables.resize(num_new_coins);
new_address_commitment_nonce_variables.resize(num_new_coins);
new_coin_serial_number_nonce_variables.resize(num_new_coins);
new_coin_value_variables.resize(num_new_coins);
for (size_t i = 0; i < num_new_coins; ++i)
{
new_address_public_key_variables[i].allocate(pb, address_public_key_length, FMT(annotation_prefix, " new_address_public_key_variables_%zu", i));
new_address_commitment_nonce_variables[i].allocate(pb, address_commitment_nonce_length, FMT(annotation_prefix, " new_address_commitment_nonce_variables_%zu", i));
new_coin_serial_number_nonce_variables[i].allocate(pb, serial_number_nonce_length, FMT(annotation_prefix, " new_coin_serial_number_nonce_variables_%zu", i));
new_coin_value_variables[i].allocate(pb, coin_value_length, FMT(annotation_prefix, " new_coin_value_variables_%zu", i));
}
old_address_secret_key_variables.resize(num_old_coins);
old_address_commitment_nonce_variables.resize(num_old_coins);
old_coin_serial_number_nonce_variables.resize(num_old_coins);
old_coin_value_variables.resize(num_old_coins);
for (size_t i = 0; i < num_old_coins; ++i)
{
old_address_secret_key_variables[i].allocate(pb, address_secret_key_length, FMT(annotation_prefix, " old_address_secret_key_variables_%zu", i));
old_address_commitment_nonce_variables[i].allocate(pb, address_commitment_nonce_length, FMT(annotation_prefix, " old_address_commitment_nonce_variables_%zu", i));
old_coin_serial_number_nonce_variables[i].allocate(pb, serial_number_nonce_length, FMT(annotation_prefix, " old_coin_serial_number_nonce_variables_%zu", i));
old_coin_value_variables[i].allocate(pb, coin_value_length, FMT(annotation_prefix, " old_coin_value_variables_%zu", i));
}
/* do the actual hashing */
pb_variable_array<FieldT> zero_one;
zero_one.emplace_back(zero);
zero_one.emplace_back(ONE);
prf_for_old_coin_serial_number_input_variables.resize(num_old_coins);
prfs_for_old_coin_serial_numbers.resize(num_old_coins);
for (size_t i = 0; i < num_old_coins; ++i)
{
/* (C) old_coin_serial_number_variables[i] = PRF_{old_address_secret_key_variables[i]}^{sn}
(old_coin_serial_number_nonce_variables[0..254]) =
H(old_address_secret_key_variables[i] || 01 || old_coin_serial_number_nonce_variables[0..254]) */
prf_for_old_coin_serial_number_input_variables[i].reset(new block_variable<FieldT>(pb, {
old_address_secret_key_variables[i],
zero_one,
pb_variable_array<FieldT>(old_coin_serial_number_nonce_variables[i].begin(),
old_coin_serial_number_nonce_variables[i].begin() + truncated_serial_number_length) },
FMT(annotation_prefix, " prf_for_old_coin_serial_number_input_variables_%zu", i)));
prfs_for_old_coin_serial_numbers[i].reset(new sha256_compression_function_gadget<FieldT>(pb, IV, prf_for_old_coin_serial_number_input_variables[i]->bits, *old_coin_serial_number_variables[i], FMT(annotation_prefix, " prfs_for_old_coin_serial_numbers_%zu", i)));
}
old_address_public_key_variables.resize(num_old_coins);
prf_for_old_address_public_key_input_variables.resize(num_old_coins);
prfs_for_old_address_public_keys.resize(num_old_coins);
for (size_t i = 0; i < num_old_coins; ++i)
{
old_address_public_key_variables[i].reset(new digest_variable<FieldT>(pb, sha256_digest_len, FMT(annotation_prefix, " old_address_public_key_variables_%zu", i)));
/* (B) old_address_public_keys[i] = PRF_{old_address_secret_key_variables[i]}^{addr}(z) =
H(old_address_secret_key_variables[i] || 00 || z), where z = 0...0 */
pb_variable_array<FieldT> addr_pk_pad(address_public_key_padding_length, zero);
prf_for_old_address_public_key_input_variables[i].reset(new block_variable<FieldT>(pb,
{ old_address_secret_key_variables[i], addr_pk_pad },
FMT(annotation_prefix, " prf_for_old_address_public_key_input_variables_%zu", i)));
prfs_for_old_address_public_keys[i].reset(new sha256_compression_function_gadget<FieldT>(pb,
IV,
prf_for_old_address_public_key_input_variables[i]->bits,
*old_address_public_key_variables[i],
FMT(annotation_prefix, " prfs_for_old_address_public_keys_%zu", i)));
}
commitments_to_old_address_public_keys.resize(num_old_coins);
commit_to_old_address_public_key_input_variables.resize(num_old_coins);
commit_to_old_address_public_keys.resize(num_old_coins);
for (size_t i = 0; i < num_old_coins; ++i)
{
/* (D0) commitments_to_old_address_public_keys[i] = H(old_address_public_key_variables[i] || old_coin_serial_number_nonce_variables[i]) */
commitments_to_old_address_public_keys[i].reset(new digest_variable<FieldT>(pb, sha256_digest_len, FMT(annotation_prefix, " commitments_to_old_address_public_keys_%zu", i)));
commit_to_old_address_public_key_input_variables[i].reset(new block_variable<FieldT>(pb, { old_address_public_key_variables[i]->bits, old_coin_serial_number_nonce_variables[i] }, FMT(annotation_prefix, " commit_to_old_address_public_key_input_variables_%zu", i)));
commit_to_old_address_public_keys[i].reset(new sha256_compression_function_gadget<FieldT>(pb, IV, commit_to_old_address_public_key_input_variables[i]->bits, *commitments_to_old_address_public_keys[i], FMT(annotation_prefix, " commit_to_old_address_public_keys_%zu", i)));
}
old_coin_value_commitment_nonces.resize(num_old_coins);
commit_to_old_coin_value_commitment_nonce_input_variables.resize(num_old_coins);
commit_to_old_coin_value_commitment_nonces.resize(num_old_coins);
for (size_t i = 0; i < num_old_coins; ++i)
{
/* (D1) old_coin_value_commitment_nonces[i] =
H(old_address_commitment_nonce_variables[i] || commitments_to_old_address_public_keys[i] [0..128]) */
old_coin_value_commitment_nonces[i].reset(new digest_variable<FieldT>(pb, sha256_digest_len, FMT(annotation_prefix, " old_coin_value_commitment_nonces_%zu", i)));
commit_to_old_coin_value_commitment_nonce_input_variables[i].reset(new block_variable<FieldT>(pb, { old_address_commitment_nonce_variables[i], pb_variable_array<FieldT>(commitments_to_old_address_public_keys[i]->bits.begin(), commitments_to_old_address_public_keys[i]->bits.begin()+ truncated_coin_commitment_length) }, FMT(annotation_prefix, " commit_to_old_coin_value_commitment_nonce_input_variables_%zu", i)));
commit_to_old_coin_value_commitment_nonces[i].reset(new sha256_compression_function_gadget<FieldT>(pb, IV, commit_to_old_coin_value_commitment_nonce_input_variables[i]->bits, *old_coin_value_commitment_nonces[i], FMT(annotation_prefix, " commit_to_old_coin_value_commitment_nonces_%zu", i)));
}
pb_variable_array<FieldT> coincomm_pad(coin_commitment_padding_length, zero);
old_coin_commitment_variables.resize(num_old_coins);
compute_old_coin_commitment_input_variables.resize(num_old_coins);
compute_old_coin_commitments.resize(num_old_coins);
for (size_t i = 0; i < num_old_coins; ++i)
{
/* (D2) old_coin_commitment_variables[i] = COMM_s(old_coin_value_variables[i] || old_coin_value_commitment_nonces[i])
H(old_coin_value_commitment_nonces[i] || 0^{192} || old_coin_value_variables[i])
Here we ignore commitment randomness s, as k = old_coin_value_commitment_nonces[i] is an output of a
statistically hiding commitment scheme. */
old_coin_commitment_variables[i].reset(new digest_variable<FieldT>(pb, sha256_digest_len, FMT(annotation_prefix, " old_coin_commitment_variables_%zu", i)));
compute_old_coin_commitment_input_variables[i].reset(new block_variable<FieldT>(pb, { old_coin_value_commitment_nonces[i]->bits, coincomm_pad, old_coin_value_variables[i] }, FMT(annotation_prefix, " compute_old_coin_commitment_input_variables_%zu", i)));
compute_old_coin_commitments[i].reset(new sha256_compression_function_gadget<FieldT>(pb, IV, compute_old_coin_commitment_input_variables[i]->bits, *old_coin_commitment_variables[i], FMT(annotation_prefix, " compute_old_coin_commitment_%zu", i)));
}
commitments_to_new_address_public_keys.resize(num_new_coins);
commit_to_new_address_public_key_input_variables.resize(num_new_coins);
commit_to_new_address_public_keys.resize(num_new_coins);
for (size_t i = 0; i < num_new_coins; ++i)
{
/* (E0) commitments_to_new_address_public_keys[i] = H(new_address_public_key_variables[i] || new_coin_serial_number_nonce_variables[i]) */
commitments_to_new_address_public_keys[i].reset(new digest_variable<FieldT>(pb, sha256_digest_len, FMT(annotation_prefix, " commitments_to_new_address_public_keys_%zu", i)));
commit_to_new_address_public_key_input_variables[i].reset(new block_variable<FieldT>(pb, { new_address_public_key_variables[i], new_coin_serial_number_nonce_variables[i] }, FMT(annotation_prefix, " commit_to_new_address_public_key_input_variables_%zu", i)));
commit_to_new_address_public_keys[i].reset(new sha256_compression_function_gadget<FieldT>(pb, IV, commit_to_new_address_public_key_input_variables[i]->bits, *commitments_to_new_address_public_keys[i], FMT(annotation_prefix, " commit_to_new_address_public_keys_%zu", i)));
}
new_coin_value_commitment_nonces.resize(num_new_coins);
commit_to_new_coin_value_commitment_nonce_input_variables.resize(num_new_coins);
commit_to_new_coin_value_commitment_nonces.resize(num_new_coins);
for (size_t i = 0; i < num_new_coins; ++i)
{
/* (E1) new_coin_value_commitment_nonces[i] =
H(new_address_commitment_nonce_variables[i] || commitments_to_new_address_public_keys[i] [0..128]) */
new_coin_value_commitment_nonces[i].reset(new digest_variable<FieldT>(pb, sha256_digest_len, FMT(annotation_prefix, " new_coin_value_commitment_nonces_%zu", i)));
commit_to_new_coin_value_commitment_nonce_input_variables[i].reset(new block_variable<FieldT>(pb, { new_address_commitment_nonce_variables[i], pb_variable_array<FieldT>(commitments_to_new_address_public_keys[i]->bits.begin(), commitments_to_new_address_public_keys[i]->bits.begin()+ truncated_coin_commitment_length) }, FMT(annotation_prefix, " commit_to_new_coin_value_commitment_nonce_input_variables_%zu", i)));
commit_to_new_coin_value_commitment_nonces[i].reset(new sha256_compression_function_gadget<FieldT>(pb, IV, commit_to_new_coin_value_commitment_nonce_input_variables[i]->bits, *new_coin_value_commitment_nonces[i], FMT(annotation_prefix, " commit_to_new_coin_value_commitment_nonces_%zu", i)));
}
compute_new_coin_commitment_input_variables.resize(num_new_coins);
compute_new_coin_commitments.resize(num_new_coins);
for (size_t i = 0; i < num_new_coins; ++i)
{
/* (E2) new_coin_commitment_variables[i] = COMM_s(new_coin_value_variables[i] || new_coin_value_commitment_nonces[i])
H(new_coin_value_commitment_nonces[i] || 0^{192} || new_coin_value_variables[i]) */
compute_new_coin_commitment_input_variables[i].reset(new block_variable<FieldT>(pb, { new_coin_value_commitment_nonces[i]->bits, coincomm_pad, new_coin_value_variables[i] }, FMT(annotation_prefix, " compute_new_coin_commitment_input_variables_%zu", i)));
compute_new_coin_commitments[i].reset(new sha256_compression_function_gadget<FieldT>(pb, IV, compute_new_coin_commitment_input_variables[i]->bits, *new_coin_commitment_variables[i], FMT(annotation_prefix, " compute_new_coin_commitment_%zu", i)));
}
/* compute signature public key macs */
prf_for_macs_of_signature_public_key_hash_input_variables.resize(num_old_coins);
prfs_for_macs_of_signature_public_key_hash.resize(num_old_coins);
const size_t truncated_signature_public_key_hash_length = indexed_signature_public_key_hash_length - log2(num_old_coins);
for (size_t i = 0; i < num_old_coins; ++i)
{
/* (F) mac_of_signature_public_key_hash_variables[i] = PRF_{old_address_secret_key_variables[i]}^{pk}
(i || signature_public_key_hash_variable) =
H(old_address_secret_key_variables[i] || 10 || i || signature_public_key_hash_variable)
Here signature_public_key_hash is truncated so that the entire argument fits inside SHA256 block.
Furthermore, the representation of i is MSB to LSB and is exactly log2(num_old_coins) bits long. */
pb_variable_array<FieldT> prf_padding;
prf_padding.emplace_back(ONE);
prf_padding.emplace_back(zero);
for (size_t j = 0; j < log2(num_old_coins); ++j)
{
prf_padding.emplace_back((i >> (log2(num_old_coins) - j - 1)) & 1 ? ONE : zero);
}
prf_for_macs_of_signature_public_key_hash_input_variables[i].reset(new block_variable<FieldT>(pb, { old_address_secret_key_variables[i], prf_padding, pb_variable_array<FieldT>(signature_public_key_hash_variable->bits.begin(), signature_public_key_hash_variable->bits.begin()+truncated_signature_public_key_hash_length) }, FMT(annotation_prefix, " prf_for_macs_of_signature_public_key_hash_input_variables_%zu", i)));
prfs_for_macs_of_signature_public_key_hash[i].reset(new sha256_compression_function_gadget<FieldT>(pb, IV, prf_for_macs_of_signature_public_key_hash_input_variables[i]->bits, *mac_of_signature_public_key_hash_variables[i], FMT(annotation_prefix, " prfs_for_macs_of_signature_public_key_hash_%zu", i)));
}
/* prove membership in the Merkle tree*/
old_coin_merkle_tree_position_variables.resize(num_old_coins);
old_coin_authentication_path_variables.resize(num_old_coins);
old_coin_commitments_in_tree.resize(num_old_coins);
for (size_t i = 0; i < num_old_coins; ++i)
{
/* (A) old_coin_commitment_variables[i] appears on path old_coin_authentication_paths[i]
to merkle_tree_root_variable */
old_coin_merkle_tree_position_variables[i].allocate(pb, tree_depth, FMT(annotation_prefix, " old_coin_merkle_tree_position_variables_%zu", i));
old_coin_authentication_path_variables[i].reset(new merkle_authentication_path_variable<FieldT, sha256_two_to_one_hash_gadget<FieldT> >(pb, tree_depth, FMT(annotation_prefix, " old_coin_authentication_path_variables_%zu", i)));
old_coin_commitments_in_tree[i].reset(new merkle_tree_check_read_gadget<FieldT, sha256_two_to_one_hash_gadget<FieldT> >(
pb, tree_depth, old_coin_merkle_tree_position_variables[i], *old_coin_commitment_variables[i], *merkle_tree_root_variable,
*old_coin_authentication_path_variables[i], old_coin_enforce_commitment[i], FMT(annotation_prefix, " old_coin_commitments_in_tree_%zu", i)));
}
}
template<typename FieldT>
void zerocash_pour_gadget<FieldT>::generate_r1cs_constraints()
{
generate_r1cs_equals_const_constraint<FieldT>(this->pb, zero, FieldT::zero(), FMT(this->annotation_prefix, " zero"));
for (size_t i = 0; i < num_old_coins; ++i)
{
prfs_for_old_coin_serial_numbers[i]->generate_r1cs_constraints();
prfs_for_old_address_public_keys[i]->generate_r1cs_constraints();
commit_to_old_address_public_keys[i]->generate_r1cs_constraints();
commit_to_old_coin_value_commitment_nonces[i]->generate_r1cs_constraints();
compute_old_coin_commitments[i]->generate_r1cs_constraints();
old_coin_commitments_in_tree[i]->generate_r1cs_constraints();
prfs_for_macs_of_signature_public_key_hash[i]->generate_r1cs_constraints();
for (size_t j = 0; j < tree_depth; ++j)
{
generate_boolean_r1cs_constraint<FieldT>(this->pb, old_coin_merkle_tree_position_variables[i][j], FMT(this->annotation_prefix, " old_coin_merkle_tree_position_variables_%zu_%zu", i, j));
}
}
for (size_t i = 0; i < num_new_coins; ++i)
{
commit_to_new_address_public_keys[i]->generate_r1cs_constraints();
commit_to_new_coin_value_commitment_nonces[i]->generate_r1cs_constraints();
compute_new_coin_commitments[i]->generate_r1cs_constraints();
}
unpack_inputs->generate_r1cs_constraints(true);
/* ensure bitness of all values */
for (size_t j = 0; j < coin_value_length; ++j)
{
for (size_t i = 0; i < num_old_coins; ++i)
{
generate_boolean_r1cs_constraint<FieldT>(this->pb, old_coin_value_variables[i][j], FMT(this->annotation_prefix, " old_coin_value_variables_%zu_%zu", i, j));
}
for (size_t i = 0; i < num_new_coins; ++i)
{
generate_boolean_r1cs_constraint<FieldT>(this->pb, new_coin_value_variables[i][j], FMT(this->annotation_prefix, " new_coin_value_variables_%zu_%zu", i, j));
}
}
for (size_t i = 0; i < num_old_coins; ++i)
{
generate_boolean_r1cs_constraint<FieldT>(this->pb, old_coin_enforce_commitment[i], FMT(this->annotation_prefix, " old_coin_enforce_commitment_%zu", i));
this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(
pb_packing_sum<FieldT>(pb_variable_array<FieldT>(old_coin_value_variables[i].rbegin(), old_coin_value_variables[i].rend())),
1 - old_coin_enforce_commitment[i],
0), FMT(this->annotation_prefix, " enforce_%zu", i));
}
/* check the balance equation */
linear_combination<FieldT> old_packed_value;
for (size_t i = 0; i < num_old_coins; ++i)
{
old_packed_value = old_packed_value + pb_packing_sum<FieldT>(pb_variable_array<FieldT>(old_coin_value_variables[i].rbegin(), old_coin_value_variables[i].rend()));
}
old_packed_value = old_packed_value + pb_packing_sum<FieldT>(pb_variable_array<FieldT>(public_old_value_variable.rbegin(), public_old_value_variable.rend()));
linear_combination<FieldT> new_packed_value;
for (size_t i = 0; i < num_new_coins; ++i)
{
new_packed_value = new_packed_value + pb_packing_sum<FieldT>(pb_variable_array<FieldT>(new_coin_value_variables[i].rbegin(), new_coin_value_variables[i].rend()));
}
new_packed_value = new_packed_value + pb_packing_sum<FieldT>(pb_variable_array<FieldT>(public_new_value_variable.rbegin(), public_new_value_variable.rend()));
this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(1, old_packed_value, new_packed_value), FMT(this->annotation_prefix, " balance"));
}
template<typename FieldT>
void zerocash_pour_gadget<FieldT>::generate_r1cs_witness(const std::vector<merkle_authentication_path> &old_coin_authentication_paths,
const std::vector<size_t> &old_coin_merkle_tree_positions,
const bit_vector &merkle_tree_root,
const std::vector<bit_vector> &new_address_public_keys,
const std::vector<bit_vector> &old_address_secret_keys,
const std::vector<bit_vector> &new_address_commitment_nonces,
const std::vector<bit_vector> &old_address_commitment_nonces,
const std::vector<bit_vector> &new_coin_serial_number_nonces,
const std::vector<bit_vector> &old_coin_serial_number_nonces,
const std::vector<bit_vector> &new_coin_values,
const bit_vector &public_old_value,
const bit_vector &public_new_value,
const std::vector<bit_vector> &old_coin_values,
const bit_vector &signature_public_key_hash)
{
/* fill in the auxiliary variables */
this->pb.val(zero) = FieldT::zero();
/* fill in the witness */
for (size_t i = 0; i < num_new_coins; ++i)
{
new_address_public_key_variables[i].fill_with_bits(this->pb, new_address_public_keys[i]);
new_address_commitment_nonce_variables[i].fill_with_bits(this->pb, new_address_commitment_nonces[i]);
}
for (size_t i = 0; i < num_old_coins; ++i)
{
old_address_secret_key_variables[i].fill_with_bits(this->pb, old_address_secret_keys[i]);
old_address_commitment_nonce_variables[i].fill_with_bits(this->pb, old_address_commitment_nonces[i]);
}
for (size_t i = 0; i < num_new_coins; ++i)
{
new_coin_serial_number_nonce_variables[i].fill_with_bits(this->pb, new_coin_serial_number_nonces[i]);
new_coin_value_variables[i].fill_with_bits(this->pb, new_coin_values[i]);
}
for (size_t i = 0; i < num_old_coins; ++i)
{
this->pb.val(old_coin_enforce_commitment[i]) = FieldT::zero();
old_coin_serial_number_nonce_variables[i].fill_with_bits(this->pb, old_coin_serial_number_nonces[i]);
old_coin_value_variables[i].fill_with_bits(this->pb, old_coin_values[i]);
for (size_t j = 0; j < coin_value_length; ++j)
{
if (old_coin_values[i][j]) {
// If any bit in the value is nonzero, the value is nonzero.
// Thus, the old coin must be committed in the tree.
this->pb.val(old_coin_enforce_commitment[i]) = FieldT::one();
break;
}
}
}
public_old_value_variable.fill_with_bits(this->pb, public_old_value);
public_new_value_variable.fill_with_bits(this->pb, public_new_value);
signature_public_key_hash_variable->generate_r1cs_witness(signature_public_key_hash);
/* do the hashing */
for (size_t i = 0; i < num_old_coins; ++i)
{
prfs_for_old_coin_serial_numbers[i]->generate_r1cs_witness();
prfs_for_old_address_public_keys[i]->generate_r1cs_witness();
commit_to_old_address_public_keys[i]->generate_r1cs_witness();
commit_to_old_coin_value_commitment_nonces[i]->generate_r1cs_witness();
compute_old_coin_commitments[i]->generate_r1cs_witness();
prfs_for_macs_of_signature_public_key_hash[i]->generate_r1cs_witness();
}
for (size_t i = 0; i < num_new_coins; ++i)
{
commit_to_new_address_public_keys[i]->generate_r1cs_witness();
commit_to_new_coin_value_commitment_nonces[i]->generate_r1cs_witness();
compute_new_coin_commitments[i]->generate_r1cs_witness();
}
/* prove the membership in the Merkle tree */
for (size_t i = 0; i < num_old_coins; ++i)
{
/* (A) old_coin_commitment_variables[i] appears on path old_coin_authentication_paths[i]
to merkle_tree_root_variable */
old_coin_merkle_tree_position_variables[i].fill_with_bits_of_ulong(this->pb, old_coin_merkle_tree_positions[i]);
old_coin_authentication_path_variables[i]->generate_r1cs_witness(old_coin_merkle_tree_positions[i], old_coin_authentication_paths[i]);
old_coin_commitments_in_tree[i]->generate_r1cs_witness();
}
/* pack the input */
unpack_inputs->generate_r1cs_witness_from_bits();
#ifdef DEBUG
printf("input_as_field_elements according to witness map:\n");
for (size_t i = 0; i < input_as_field_elements.size(); ++i)
{
this->pb.val(input_as_field_elements[i]).print();
}
#endif
}
template<typename FieldT>
r1cs_primary_input<FieldT> zerocash_pour_input_map(const size_t num_old_coins,
const size_t num_new_coins,
const bit_vector &merkle_tree_root,
const std::vector<bit_vector> &old_coin_serial_numbers,
const std::vector<bit_vector> &new_coin_commitments,
const bit_vector &public_old_value,
const bit_vector &public_new_value,
const bit_vector &signature_public_key_hash,
const std::vector<bit_vector> &signature_public_key_hash_macs)
{
enter_block("Call to zerocash_pour_input_map");
assert(merkle_tree_root.size() == sha256_digest_len);
assert(old_coin_serial_numbers.size() == num_old_coins);
for (auto &old_coin_serial_number : old_coin_serial_numbers)
{
assert(old_coin_serial_number.size() == serial_number_length);
}
assert(new_coin_commitments.size() == num_new_coins);
for (auto &new_coin_commitment : new_coin_commitments)
{
assert(new_coin_commitment.size() == coin_commitment_length);
}
assert(public_old_value.size() == coin_value_length);
assert(public_new_value.size() == coin_value_length);
assert(signature_public_key_hash.size() == sha256_digest_len);
assert(signature_public_key_hash_macs.size() == num_old_coins);
for (auto &signature_public_key_hash_mac : signature_public_key_hash_macs)
{
assert(signature_public_key_hash_mac.size() == sha256_digest_len);
}
bit_vector input_as_bits;
input_as_bits.insert(input_as_bits.end(), merkle_tree_root.begin(), merkle_tree_root.end());
for (auto &old_coin_serial_number : old_coin_serial_numbers)
{
input_as_bits.insert(input_as_bits.end(), old_coin_serial_number.begin(), old_coin_serial_number.end());
}
for (auto &new_coin_commitment : new_coin_commitments)
{
input_as_bits.insert(input_as_bits.end(), new_coin_commitment.begin(), new_coin_commitment.end());
}
input_as_bits.insert(input_as_bits.end(), public_old_value.begin(), public_old_value.end());
input_as_bits.insert(input_as_bits.end(), public_new_value.begin(), public_new_value.end());
input_as_bits.insert(input_as_bits.end(), signature_public_key_hash.begin(), signature_public_key_hash.end());
for (auto &signature_public_key_hash_mac : signature_public_key_hash_macs)
{
input_as_bits.insert(input_as_bits.end(), signature_public_key_hash_mac.begin(), signature_public_key_hash_mac.end());
}
std::vector<FieldT> input_as_field_elements = pack_bit_vector_into_field_element_vector<FieldT>(input_as_bits);
#ifdef DEBUG
printf("input_as_field_elements from zerocash_pour_input_map:\n");
for (size_t i = 0; i < input_as_field_elements.size(); ++i)
{
input_as_field_elements[i].print();
}
#endif
leave_block("Call to zerocash_pour_input_map");
return input_as_field_elements;
}
} // libzerocash

View File

@ -1,34 +0,0 @@
/** @file
*****************************************************************************
Declaration of various parameters used by the Pour gadget and Pour ppzkSNARK.
*****************************************************************************
* @author This file is part of libzerocash, developed by the Zerocash
* project and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef ZEROCASH_POUR_PARAMS_HPP_
#define ZEROCASH_POUR_PARAMS_HPP_
namespace libzerocash {
const size_t sha256_block_len = 512;
const size_t sha256_digest_len = 256;
const size_t address_public_key_length = sha256_digest_len;
const size_t address_public_key_padding_length = 256;
const size_t address_secret_key_length = 256;
const size_t coin_commitment_length = sha256_digest_len;
const size_t coin_commitment_padding_length = 192;
const size_t truncated_coin_commitment_length = 128;
const size_t truncated_serial_number_length = 254;
const size_t serial_number_length = sha256_digest_len;
const size_t address_commitment_nonce_length = 384;
const size_t serial_number_nonce_length = 256;
const size_t coin_value_length = 64;
const size_t indexed_signature_public_key_hash_length = 254;
} // libzerocash
#endif // ZEROCASH_POUR_PARAMS_HPP_

View File

@ -1,232 +0,0 @@
/** @file
*****************************************************************************
Declaration of interfaces for a ppzkSNARK for the NP statement "Pour".
This includes:
- class for proving key
- class for verification key
- class for key pair (proving key & verification key)
- class for proof
- generator algorithm
- prover algorithm
- verifier algorithm
The ppzkSNARK is obtained by using an R1CS ppzkSNARK relative to an R1CS
realization of the NP statement "Pour". The implementation follows, extends,
and optimizes the approach described in \[BCGGMTV14].
Acronyms:
- R1CS = "Rank-1 Constraint Systems"
- ppzkSNARK = "PreProcessing Zero-Knowledge Succinct Non-interactive ARgument of Knowledge"
References:
\[BCGGMTV14]:
"Zerocash: Decentralized Anonymous Payments from Bitcoin",
Eli Ben-Sasson, Alessandro Chiesa, Christina Garman, Matthew Green, Ian Miers, Eran Tromer, Madars Virza,
S&P 2014,
<https://eprint.iacr.org/2014/349>
*****************************************************************************
* @author This file is part of libzerocash, developed by the Zerocash
* project and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef ZEROCASH_POUR_PPZKSNARK_HPP_
#define ZEROCASH_POUR_PPZKSNARK_HPP_
#include "libsnark/common/data_structures/merkle_tree.hpp"
#include "libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp"
#include <stdexcept>
namespace libzerocash {
/******************************** Proving key ********************************/
/**
* A proving key for the Pour ppzkSNARK.
*/
template<typename ppzksnark_ppT>
class zerocash_pour_proving_key;
template<typename ppzksnark_ppT>
std::ostream& operator<<(std::ostream &out, const zerocash_pour_proving_key<ppzksnark_ppT> &pk);
template<typename ppzksnark_ppT>
std::istream& operator>>(std::istream &in, zerocash_pour_proving_key<ppzksnark_ppT> &pk);
template<typename ppzksnark_ppT>
class zerocash_pour_proving_key {
public:
size_t num_old_coins;
size_t num_new_coins;
size_t tree_depth;
r1cs_ppzksnark_proving_key<ppzksnark_ppT> r1cs_pk;
zerocash_pour_proving_key() = default;
zerocash_pour_proving_key(const zerocash_pour_proving_key<ppzksnark_ppT> &other) = default;
zerocash_pour_proving_key(zerocash_pour_proving_key<ppzksnark_ppT> &&other) = default;
zerocash_pour_proving_key(const size_t num_old_coins,
const size_t num_new_coins,
const size_t tree_depth,
const r1cs_ppzksnark_proving_key<ppzksnark_ppT> &r1cs_pk) :
num_old_coins(num_old_coins), num_new_coins(num_new_coins),
tree_depth(tree_depth), r1cs_pk(r1cs_pk) {}
zerocash_pour_proving_key(const size_t num_old_coins,
const size_t num_new_coins,
const size_t tree_depth,
r1cs_ppzksnark_proving_key<ppzksnark_ppT> &&r1cs_pk) :
num_old_coins(num_old_coins), num_new_coins(num_new_coins),
tree_depth(tree_depth), r1cs_pk(std::move(r1cs_pk)) {}
zerocash_pour_proving_key<ppzksnark_ppT>& operator=(const zerocash_pour_proving_key<ppzksnark_ppT> &other) = default;
bool operator==(const zerocash_pour_proving_key<ppzksnark_ppT> &other) const;
friend std::ostream& operator<< <ppzksnark_ppT>(std::ostream &out, const zerocash_pour_proving_key<ppzksnark_ppT> &pk);
friend std::istream& operator>> <ppzksnark_ppT>(std::istream &in, zerocash_pour_proving_key<ppzksnark_ppT> &pk);
};
/******************************* Verification key ****************************/
/**
* A verification key for the Pour ppzkSNARK.
*/
template<typename ppzksnark_ppT>
class zerocash_pour_verification_key;
template<typename ppzksnark_ppT>
std::ostream& operator<<(std::ostream &out, const zerocash_pour_verification_key<ppzksnark_ppT> &pk);
template<typename ppzksnark_ppT>
std::istream& operator>>(std::istream &in, zerocash_pour_verification_key<ppzksnark_ppT> &pk);
template<typename ppzksnark_ppT>
class zerocash_pour_verification_key {
public:
size_t num_old_coins;
size_t num_new_coins;
r1cs_ppzksnark_verification_key<ppzksnark_ppT> r1cs_vk;
zerocash_pour_verification_key() = default;
zerocash_pour_verification_key(const zerocash_pour_verification_key<ppzksnark_ppT> &other) = default;
zerocash_pour_verification_key(zerocash_pour_verification_key<ppzksnark_ppT> &&other) = default;
zerocash_pour_verification_key(const size_t num_old_coins,
const size_t num_new_coins,
const r1cs_ppzksnark_verification_key<ppzksnark_ppT> &r1cs_vk) :
num_old_coins(num_old_coins), num_new_coins(num_new_coins),
r1cs_vk(r1cs_vk) {}
zerocash_pour_verification_key(const size_t num_old_coins,
const size_t num_new_coins,
r1cs_ppzksnark_verification_key<ppzksnark_ppT> &&r1cs_vk) :
num_old_coins(num_old_coins), num_new_coins(num_new_coins),
r1cs_vk(std::move(r1cs_vk)) {}
zerocash_pour_verification_key<ppzksnark_ppT>& operator=(const zerocash_pour_verification_key<ppzksnark_ppT> &other) = default;
bool operator==(const zerocash_pour_verification_key<ppzksnark_ppT> &other) const;
friend std::ostream& operator<< <ppzksnark_ppT>(std::ostream &out, const zerocash_pour_verification_key<ppzksnark_ppT> &pk);
friend std::istream& operator>> <ppzksnark_ppT>(std::istream &in, zerocash_pour_verification_key<ppzksnark_ppT> &pk);
};
/********************************** Key pair *********************************/
/**
* A key pair for the Pour ppzkSNARK, which consists of a proving key and a verification key.
*/
template<typename ppzksnark_ppT>
class zerocash_pour_keypair;
template<typename ppzksnark_ppT>
std::ostream& operator<<(std::ostream &out, const zerocash_pour_keypair<ppzksnark_ppT> &pk);
template<typename ppzksnark_ppT>
std::istream& operator>>(std::istream &in, zerocash_pour_keypair<ppzksnark_ppT> &pk);
template<typename ppzksnark_ppT>
class zerocash_pour_keypair {
public:
zerocash_pour_proving_key<ppzksnark_ppT> pk;
zerocash_pour_verification_key<ppzksnark_ppT> vk;
zerocash_pour_keypair() = default;
zerocash_pour_keypair(const zerocash_pour_keypair<ppzksnark_ppT> &other) = default;
zerocash_pour_keypair(zerocash_pour_keypair<ppzksnark_ppT> &&other) = default;
zerocash_pour_keypair(const zerocash_pour_proving_key<ppzksnark_ppT> &pk,
const zerocash_pour_verification_key<ppzksnark_ppT> &vk) :
pk(pk), vk(vk) {}
zerocash_pour_keypair(zerocash_pour_proving_key<ppzksnark_ppT> &&pk,
zerocash_pour_verification_key<ppzksnark_ppT> &&vk) :
pk(std::move(pk)),
vk(std::move(vk)) {}
zerocash_pour_keypair<ppzksnark_ppT>& operator=(const zerocash_pour_keypair<ppzksnark_ppT> &other) = default;
bool operator==(const zerocash_pour_keypair<ppzksnark_ppT> &other) const;
friend std::ostream& operator<< <ppzksnark_ppT>(std::ostream &out, const zerocash_pour_keypair<ppzksnark_ppT> &pk);
friend std::istream& operator>> <ppzksnark_ppT>(std::istream &in, zerocash_pour_keypair<ppzksnark_ppT> &pk);
};
/*********************************** Proof ***********************************/
/**
* A proof for the Pour ppzkSNARK.
*/
template<typename ppzksnark_ppT>
using zerocash_pour_proof = r1cs_ppzksnark_proof<ppzksnark_ppT>;
/***************************** Main algorithms *******************************/
/**
* A generator algorithm for the Pour ppzkSNARK.
*
* Given a tree depth d, this algorithm produces proving and verification keys
* for Pour relative to a Merkle tree of depth d.
*/
template<typename ppzksnark_ppT>
zerocash_pour_keypair<ppzksnark_ppT> zerocash_pour_ppzksnark_generator(const size_t num_old_coins,
const size_t num_new_coins,
const size_t tree_depth);
/**
* A prover algorithm for the Pour ppzkSNARK.
*
* TODO: add description
*/
template<typename ppzksnark_ppT>
zerocash_pour_proof<ppzksnark_ppT> zerocash_pour_ppzksnark_prover(const zerocash_pour_proving_key<ppzksnark_ppT> &pk,
const std::vector<merkle_authentication_path> &old_coin_authentication_paths,
const std::vector<size_t> &old_coin_merkle_tree_positions,
const bit_vector &merkle_tree_root,
const std::vector<bit_vector> &new_address_public_keys,
const std::vector<bit_vector> &old_address_secret_keys,
const std::vector<bit_vector> &new_address_commitment_nonces,
const std::vector<bit_vector> &old_address_commitment_nonces,
const std::vector<bit_vector> &new_coin_serial_number_nonces,
const std::vector<bit_vector> &old_coin_serial_number_nonces,
const std::vector<bit_vector> &new_coin_values,
const bit_vector &public_old_value,
const bit_vector &public_new_value,
const std::vector<bit_vector> &old_coin_values,
const bit_vector &signature_public_key_hash);
/**
* A verifier algorithm for the Pour ppzkSNARK.
*/
template<typename ppzksnark_ppT>
bool zerocash_pour_ppzksnark_verifier(const zerocash_pour_verification_key<ppzksnark_ppT> &vk,
const bit_vector &merkle_tree_root,
const std::vector<bit_vector> &old_coin_serial_numbers,
const std::vector<bit_vector> &new_coin_commitments,
const bit_vector &public_old_value,
const bit_vector &public_new_value,
const bit_vector &signature_public_key_hash,
const std::vector<bit_vector> &signature_public_key_hash_macs,
const zerocash_pour_proof<ppzksnark_ppT> &proof);
} // libzerocash
#include "zerocash_pour_ppzksnark.tcc"
#endif // ZEROCASH_POUR_PPZKSNARK_HPP_

View File

@ -1,212 +0,0 @@
/** @file
*****************************************************************************
Implementation of interfaces for a ppzkSNARK for the NP statement "Pour".
See zerocash_pour_ppzksnark.hpp .
*****************************************************************************
* @author This file is part of libzerocash, developed by the Zerocash
* project and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef ZEROCASH_POUR_PPZKSNARK_TCC_
#define ZEROCASH_POUR_PPZKSNARK_TCC_
#include "zerocash_pour_gadget.hpp"
#include "common/profiling.hpp"
namespace libzerocash {
template<typename ppzksnark_ppT>
bool zerocash_pour_proving_key<ppzksnark_ppT>::operator==(const zerocash_pour_proving_key<ppzksnark_ppT> &other) const
{
return (this->num_old_coins == other.num_old_coins &&
this->num_new_coins == other.num_new_coins &&
this->tree_depth == other.tree_depth &&
this->r1cs_pk == other.r1cs_pk);
}
template<typename ppzksnark_ppT>
std::ostream& operator<<(std::ostream &out, const zerocash_pour_proving_key<ppzksnark_ppT> &pk)
{
out << pk.num_old_coins << "\n";
out << pk.num_new_coins << "\n";
out << pk.tree_depth << "\n";
out << pk.r1cs_pk;
return out;
}
template<typename ppzksnark_ppT>
std::istream& operator>>(std::istream &in, zerocash_pour_proving_key<ppzksnark_ppT> &pk)
{
in >> pk.num_old_coins;
consume_newline(in);
in >> pk.num_new_coins;
consume_newline(in);
in >> pk.tree_depth;
consume_newline(in);
in >> pk.r1cs_pk;
return in;
}
template<typename ppzksnark_ppT>
bool zerocash_pour_verification_key<ppzksnark_ppT>::operator==(const zerocash_pour_verification_key<ppzksnark_ppT> &other) const
{
return (this->num_old_coins == other.num_old_coins &&
this->num_new_coins == other.num_new_coins &&
this->r1cs_vk == other.r1cs_vk);
}
template<typename ppzksnark_ppT>
std::ostream& operator<<(std::ostream &out, const zerocash_pour_verification_key<ppzksnark_ppT> &vk)
{
out << vk.num_old_coins << "\n";
out << vk.num_new_coins << "\n";
out << vk.r1cs_vk;
return out;
}
template<typename ppzksnark_ppT>
std::istream& operator>>(std::istream &in, zerocash_pour_verification_key<ppzksnark_ppT> &vk)
{
in >> vk.num_old_coins;
consume_newline(in);
in >> vk.num_new_coins;
consume_newline(in);
in >> vk.r1cs_vk;
return in;
}
template<typename ppzksnark_ppT>
bool zerocash_pour_keypair<ppzksnark_ppT>::operator==(const zerocash_pour_keypair<ppzksnark_ppT> &other) const
{
return (this->pk == other.pk &&
this->vk == other.vk);
}
template<typename ppzksnark_ppT>
std::ostream& operator<<(std::ostream &out, const zerocash_pour_keypair<ppzksnark_ppT> &kp)
{
out << kp.pk;
out << kp.vk;
return out;
}
template<typename ppzksnark_ppT>
std::istream& operator>>(std::istream &in, zerocash_pour_keypair<ppzksnark_ppT> &kp)
{
in >> kp.pk;
in >> kp.vk;
return in;
}
template<typename ppzksnark_ppT>
zerocash_pour_keypair<ppzksnark_ppT> zerocash_pour_ppzksnark_generator(const size_t num_old_coins,
const size_t num_new_coins,
const size_t tree_depth)
{
typedef Fr<ppzksnark_ppT> FieldT;
enter_block("Call to zerocash_pour_ppzksnark_generator");
protoboard<FieldT> pb;
zerocash_pour_gadget<FieldT> g(pb, num_old_coins, num_new_coins, tree_depth, "zerocash_pour");
g.generate_r1cs_constraints();
const r1cs_constraint_system<FieldT> constraint_system = pb.get_constraint_system();
r1cs_ppzksnark_keypair<ppzksnark_ppT> ppzksnark_keypair = r1cs_ppzksnark_generator<ppzksnark_ppT>(constraint_system);
leave_block("Call to zerocash_pour_ppzksnark_generator");
zerocash_pour_proving_key<ppzksnark_ppT> zerocash_pour_pk(num_old_coins, num_new_coins, tree_depth, std::move(ppzksnark_keypair.pk));
zerocash_pour_verification_key<ppzksnark_ppT> zerocash_pour_vk(num_old_coins, num_new_coins, std::move(ppzksnark_keypair.vk));
return zerocash_pour_keypair<ppzksnark_ppT>(std::move(zerocash_pour_pk), std::move(zerocash_pour_vk));
}
template<typename ppzksnark_ppT>
zerocash_pour_proof<ppzksnark_ppT> zerocash_pour_ppzksnark_prover(const zerocash_pour_proving_key<ppzksnark_ppT> &pk,
const std::vector<merkle_authentication_path> &old_coin_authentication_paths,
const std::vector<size_t> &old_coin_merkle_tree_positions,
const bit_vector &merkle_tree_root,
const std::vector<bit_vector> &new_address_public_keys,
const std::vector<bit_vector> &old_address_secret_keys,
const std::vector<bit_vector> &new_address_commitment_nonces,
const std::vector<bit_vector> &old_address_commitment_nonces,
const std::vector<bit_vector> &new_coin_serial_number_nonces,
const std::vector<bit_vector> &old_coin_serial_number_nonces,
const std::vector<bit_vector> &new_coin_values,
const bit_vector &public_old_value,
const bit_vector &public_new_value,
const std::vector<bit_vector> &old_coin_values,
const bit_vector &signature_public_key_hash)
{
typedef Fr<ppzksnark_ppT> FieldT;
enter_block("Call to zerocash_pour_ppzksnark_prover");
protoboard<FieldT> pb;
zerocash_pour_gadget<FieldT > g(pb, pk.num_old_coins, pk.num_new_coins, pk.tree_depth, "zerocash_pour");
g.generate_r1cs_constraints();
g.generate_r1cs_witness(old_coin_authentication_paths,
old_coin_merkle_tree_positions,
merkle_tree_root,
new_address_public_keys,
old_address_secret_keys,
new_address_commitment_nonces,
old_address_commitment_nonces,
new_coin_serial_number_nonces,
old_coin_serial_number_nonces,
new_coin_values,
public_old_value,
public_new_value,
old_coin_values,
signature_public_key_hash);
if (!pb.is_satisfied()) {
leave_block("Call to zerocash_pour_ppzksnark_prover");
throw std::invalid_argument("Constraints not satisfied by inputs");
}
zerocash_pour_proof<ppzksnark_ppT> proof = r1cs_ppzksnark_prover<ppzksnark_ppT>(pk.r1cs_pk, pb.primary_input(), pb.auxiliary_input());
leave_block("Call to zerocash_pour_ppzksnark_prover");
return proof;
}
template<typename ppzksnark_ppT>
bool zerocash_pour_ppzksnark_verifier(const zerocash_pour_verification_key<ppzksnark_ppT> &vk,
const bit_vector &merkle_tree_root,
const std::vector<bit_vector> &old_coin_serial_numbers,
const std::vector<bit_vector> &new_coin_commitments,
const bit_vector &public_old_value,
const bit_vector &public_new_value,
const bit_vector &signature_public_key_hash,
const std::vector<bit_vector> &signature_public_key_hash_macs,
const zerocash_pour_proof<ppzksnark_ppT> &proof)
{
typedef Fr<ppzksnark_ppT> FieldT;
enter_block("Call to zerocash_pour_ppzksnark_verifier");
const r1cs_primary_input<FieldT> input = zerocash_pour_input_map<FieldT>(vk.num_old_coins,
vk.num_new_coins,
merkle_tree_root,
old_coin_serial_numbers,
new_coin_commitments,
public_old_value,
public_new_value,
signature_public_key_hash,
signature_public_key_hash_macs);
const bool ans = r1cs_ppzksnark_verifier_strong_IC<ppzksnark_ppT>(vk.r1cs_vk, input, proof);
leave_block("Call to zerocash_pour_ppzksnark_verifier");
return ans;
}
} // libzerocash
#endif // ZEROCASH_POUR_PPZKSNARK_TCC_

View File

@ -4,8 +4,8 @@ set -eu
PARAMS_DIR="$HOME/.zcash-params"
REGTEST_PKEY_NAME='zc-testnet-public-alpha-proving.key'
REGTEST_VKEY_NAME='zc-testnet-public-alpha-verification.key'
REGTEST_PKEY_NAME='z3-proving.key'
REGTEST_VKEY_NAME='z3-verification.key'
REGTEST_PKEY_URL="https://z.cash/downloads/$REGTEST_PKEY_NAME"
REGTEST_VKEY_URL="https://z.cash/downloads/$REGTEST_VKEY_NAME"
REGTEST_DIR="$PARAMS_DIR/regtest"
@ -86,9 +86,9 @@ cd "$PARAMS_DIR"
# Now verify their hashes:
echo 'Verifying parameter file integrity via sha256sum...'
sha256sum --check - <<EOF
7844a96933979158886a5b69fb163f49de76120fa1dcfc33b16c83c134e61817 regtest/$REGTEST_PKEY_NAME
7844a96933979158886a5b69fb163f49de76120fa1dcfc33b16c83c134e61817 testnet3/$REGTEST_PKEY_NAME
6902fd687bface72e572a7cda57f6da5a0c606c7b9769f30becd255e57924f41 regtest/$REGTEST_VKEY_NAME
6902fd687bface72e572a7cda57f6da5a0c606c7b9769f30becd255e57924f41 testnet3/$REGTEST_VKEY_NAME
1f16beeafe4f0a22cc6d0ea07bdacb083dd10bfd5ce755f72fb5eaeba0ba7286 regtest/$REGTEST_PKEY_NAME
1f16beeafe4f0a22cc6d0ea07bdacb083dd10bfd5ce755f72fb5eaeba0ba7286 testnet3/$REGTEST_PKEY_NAME
3840f3192c987a032fc1855e0a6081b62ae9df98172c9d68e7ecf8bb38b18426 regtest/$REGTEST_VKEY_NAME
3840f3192c987a032fc1855e0a6081b62ae9df98172c9d68e7ecf8bb38b18426 testnet3/$REGTEST_VKEY_NAME
EOF