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:
commit
9e387120eb
|
@ -10,12 +10,16 @@ src/test/test_bitcoin
|
||||||
src/qt/test/test_bitcoin-qt
|
src/qt/test/test_bitcoin-qt
|
||||||
|
|
||||||
# zerocash tests and utilities
|
# zerocash tests and utilities
|
||||||
|
src/zcash/GenerateParams
|
||||||
src/zerocash/GenerateParamsForFiles
|
src/zerocash/GenerateParamsForFiles
|
||||||
src/zerocash/tests/merkleTest
|
src/zerocash/tests/merkleTest
|
||||||
src/zerocash/tests/utilTest
|
src/zerocash/tests/utilTest
|
||||||
src/zerocash/tests/zerocashTest
|
src/zerocash/tests/zerocashTest
|
||||||
src/zerocash/tests/test_zerocash_pour_ppzksnark
|
src/zerocash/tests/test_zerocash_pour_ppzksnark
|
||||||
|
|
||||||
|
*zcashTest.pk
|
||||||
|
*zcashTest.vk
|
||||||
|
|
||||||
# autoreconf
|
# autoreconf
|
||||||
Makefile.in
|
Makefile.in
|
||||||
aclocal.m4
|
aclocal.m4
|
||||||
|
|
|
@ -38,9 +38,6 @@ run_test_phase make check '||' \
|
||||||
false ';' \
|
false ';' \
|
||||||
'}'
|
'}'
|
||||||
|
|
||||||
run_test_phase "${REPOROOT}/src/zerocash/tests/zerocashTest"
|
|
||||||
run_test_phase "${REPOROOT}/src/zerocash/tests/test_zerocash_pour_ppzksnark"
|
|
||||||
|
|
||||||
exit $SUITE_EXIT_STATUS
|
exit $SUITE_EXIT_STATUS
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ function zcashd_massif_stop {
|
||||||
ms_print massif.out
|
ms_print massif.out
|
||||||
}
|
}
|
||||||
|
|
||||||
RAWTXWITHPOUR=02000000016939161447690d28bb07215b39ae6814bc7fca7a25f11650c0545eced95e365e00000000484730440220309ff19ffdabbfdcaf172c876a8f89b417d15c76a1f821fc2bfff4027586e29602202d0df763fe8cbc133624774e45b123d2e9dc90a6eb875addd4ccb0cb1c0419fc01ffffffff0000000000018091d2ed000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002924bcb835eb38c98d7fc741b9f5058090492b2cca5484446d654cbcc45f664410afafd7c8dc6a971cabf3dbd7786cbffb8cf5cd56c381fae1be232d767329d110aee171f8043d9c2173d2de8d82c700cbf212d545ecbac92ee92f5c9520bb20e82c931a64c8ec14074af68eca1801283d46197c320f640f94b71afc6ce899dff953425c608c1dd6daa461362360245183c17122d2b153329c685d8a79c512f287cfc02b32e723a98ca7657e21fca74884045331403819897fa3b757f3eded5f547ac1b9be65bb909b00339a6c616f92904475336155705b8b69a08371ab3d5567a65b2ca10fe40901767354f8d78c26349650c534586241a4328a406da80f7527f80c4459ca9e41765dedc933100a84f0d3279e409e0a2fc764dfeb2e1095756c4e02169a4eef5b1a5ff9e154c3408cba9fad363c2f0f63f22233ab266fb7ee4e207d8b492daf85b9f86da636b225a63354815010e7a7cd36395134b87d291f2afcc7f24e85374b15e3a411e686038dfbd652eda123b0e7dd6fb83c84d0fe48d10757d1760cd25d8c1bf08a609ea1892ba0c367640b40fee3dcbbaa0a9acd0802ee706de37771c35245a4c1d658db79974e96073407e093b55e730d31016a5e72aa68e3cbd46fd7e087a5311143299fc0987a39982154d40b4b901e22d7afd6e63e543033411105cc6196ed7c0b15424795a9d9ee688909269e5cd44e34b057ea44e9ce0af9a2b2bf5a152fdc45d0f88b728b8133e352f41ed29bf9b2c791860b85acb9ebd39c57fbd65852232b945c0eb8029b972c534dbb7776ded36e32a4ca2105a848232b28a94842f17f9f822f4951054a98c2dd17063597a2c98333bd229a8082093ccc1d763841c341fd605eed5567a4d1bea8487b748002ef6dcdf72ee6043ee294b9182425bafe256751b1d1e813e7f07b8deead9a0be1101e20157fa78cddd2a3902b0fbbb9560352932afd86053020313331383238313537353434373932353434313139313534313739353638353931323731373537393730343539333530323239373031303839383234353733323939303339303630303230353320313637393735353535373337303738353038323038343136333038323338353037323837373932323634313939353232363538313335343735323436393837383934363838363830333732313320302032303634363938313832343636383136343736333439383637323936383337333938313535383336393734363136393233313036313632343339333130393933353132333234393239313735312032303936333639393934373532383338383930393237333532323239393437343930363239373938333536363036393837333833373230313830343839373433333535303936373432343534320a3020313437353035363735303337373337313535393031313233383834323539363032353330383531363837333437393038333834333032393538333335373539363730383339363632353538313620313532323232303839343439323735393031353132303333393931363233303430303038323536373037343830393436333638393532373837373038333335363238343230363139383133353320333138373235353831303334353533313235303839303931383535363430353236333434333534323637393831363039353032333035353733393336313734373733333737333331363532302031373030383032353631393739383733303934313830323135343731303135373433373536313833353632323035393234313836393833363336313733363536343139343139313438313435203020323134373836363032383538303031353339373532363933373535363237383536393333353039313937373034333134383831303537383638383430333832333037323538343438353337322031313331373839313630393538303635323030383130383032323539353136333532313733363932343839363131363232393238363736323933393532303032363237383530363636383731390a302031373639363736313335353231393739343635353034363532343237313237353036333734313531353734363437383730343238303137363334363939393134303338343835383235393937372038323338353937303434343834383739303031383030363131393538323239373732363835333835363832353634363534373834393438333038353533373137353235343235313235363437203020313932393935303230343630393332303635303038323732333937363835363735363332393133373836343334343230353834373238333637393635353630333733383334373337303735343220353138343739303934393431393533303736323338343638353431363231363831393331373239333238393433343136303836323232343938343938343934393132383433313938333836330a302031363937353431353932323334393735343434393138373530333135303631353637303431363531343435353936393439343233383935383333373635363030303931343634383730353332372031313332373338303536323637353438383035313232373838313730313235313131353438393630393934323137313231343232363139303132353132363039323235323337353436373737370a3020313634373338353238383338383431373132353639393134333330323436373437343038343233303237313739323232313139303237383637333438303537303031303437323032323734343120343134373933323338343537393731343135323039353631383331373931383534323835363539363130373634373537323938313033343839383938353138323536353137323331383636300a
|
RAWTXWITHPOUR=020000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024c7ec193fc98ef10d3cf232d795dd44a8f2c8ab94fd25d136ccc86d118d412ff17838c9497c3d388ec07fbdc99a37c2854a3b6b87b1b4d68bfa7c35bab45aeaf1e34486be8d6b2e0d98a063c277001914ef50dddee4e2215ee58eb3416ffdfdead5ddb03804822868b676fb87d56a34c24b6f7facc476fa3acba6bfe81f7d7dda6be52f7ce9c7e863a1d9177907521f4cf3012b5534edeeb391c92207269a08c3bea8ebc33ab251a6a0ae8407fc1a7ab6e8be36e04dfa8f143a58963133c775f04b154ef7b41107d73d6f2a8fc3cab14ffc44376b712ec7714b4e121dd418f51ed8c6a599ac50ff3e696781bec94ac11d8065f915a0abd6656439f4a7fbc55ff0efcae1403a9d9001504cd167ca97c4ecf9d8f8904ab4d310ac84084c62f04a100423dd933be1c7b33d13bbe22f93aaccb0f79854a739fc1ce38bd13e0a6b6ad3eddac4d457eeb6dff47bc4ebc04428437a9e7e2dd71ac1f631b619c30361f7045aefe8cac34d9bd1f47e8f598cea7b4fe212d81cfcdd69f29e543e846cba4c1a6bae736e85b76f33d3431cbca19bd2243ccac6c30434d564f8920fc597bec7d202d95d010a920df18df9c3e8e15950d105809c7e5d57eb780875df5cc22f9aece88561c03a3da4d70cceef7ade85ed071ebc5e0ca3e166d6d82dfcf79bdef0ac0c02b9b716ea10d9d5daa4af992660c7128191d5487edc9a0951ee3c65a14baa1974db470e783d91e5114e4579608a1bdcce715050650231a952890cce01226f5d6f2c8b1d8868ee51deeaecef0da4f291df1e4e6d2a1e7fc9c00bef293f458f9e4391b396c03e453522f3188d8d0cf12f1d7f69780563ff449592c9b0249ffe6f8ae9f2167acf5094a6fa08f07f5bb36fbe0a94139a090305488923ba108213916f8d3b9de21586b952938b5a2563c3055fc3804581abc9dad45fb3721ea000f835dd7560bf686b057736710106113006fd88053020313436373432373635323732303533373131313331313437363032353839383932363036333330313738353230313030303933373539373534333233363030363530363833303136323538363720313732373130363635383738303430393731313235353835363637383734363934373030393830323731303036313532353732303437393539333437313236373330323734353136393337353720302032303831383832343535373436343637313436323832373235363234393531303135393935343736383438363836383638393133323136373337393239343737353032353533373639383932302031343537363435373732313933313534383536333934303732333434343431313731363130393935313436303035343138323939383036333039313735373039313230353237333636373634350a3020313837333133313335393838363137383534313838333832313234303539363438333132373935313637323436313734363133353930323235383536323436353938323037373136363739353120373731303539343839353436353331343133353635323631363637383731333832363139323534333033353333303230343038353532353435373936343330323238383038353936333035372033393238323630343737373833363032333631383533393033303532383933393434343639383238313430343235303637373630303232393931303339383538323436313031323932393832203231353835373031383531313237343139373433383336383538353733393333323238313230343538343437393833343735393537393736363530373433343731343330383036373331343039203020323037303431373036353339393033313034353239313333393435343035353132383737363534363335303837333334343636343630373930383534343139333334353738343337363538363220343938303931373435383431353030373738343035323137393136333836303031373634313737303433313530343538333637323939373935313736313430383037323933333935363334320a3020313734373431323532313139313134303139323333373338363330373430313233333634363136353539373831333735393639383135313532353934383634383235383036393930363437343620313430393930383734353239353830393935373937393830333038363439313232303132343230393138353332353735393632333832303933383438363636373435373237343739303439383820302031313535303936313835373236323531303431343530393633383238313537363737393233303838383231303632343931363433323032353035393039313630363737333538303236383430392031333537363330343835343433323031353937373136363735343430333233313033393830313734363739333035393630363933393635303931393436353038373130363732303132393531340a302032313737383334373037373231393833313033323139383636353536353838393039313934303138303939393537363830333435383838353232383735373336343231343538343436353731362031333531363133333832303232393737353734363930393130393832383932313435303238313134333539343930383433343137373135363635353135313736303536313437373131383638340a3020313939343535383534323238373237313134353535393633303332353932373130313136323337343831333731333931323838383538343530383933343236333238383039363231333337343020393631333030303238303530383434343630373235353637353935333130393431323539313435343637393338393838333533353434313635313239353434383034363238323939363934350a
|
||||||
|
|
||||||
case "$1" in
|
case "$1" in
|
||||||
time)
|
time)
|
||||||
|
|
|
@ -72,21 +72,13 @@ endif
|
||||||
# TODO: rename to libzcash
|
# TODO: rename to libzcash
|
||||||
LIBZEROCASH_H = \
|
LIBZEROCASH_H = \
|
||||||
zcash/IncrementalMerkleTree.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 \
|
zerocash/utils/util.h \
|
||||||
zcash/NoteEncryption.hpp \
|
zcash/NoteEncryption.hpp \
|
||||||
zcash/prf.h
|
zcash/Address.hpp \
|
||||||
|
zcash/JoinSplit.hpp \
|
||||||
|
zcash/Note.hpp \
|
||||||
|
zcash/prf.h \
|
||||||
|
zcash/util.h
|
||||||
|
|
||||||
.PHONY: FORCE
|
.PHONY: FORCE
|
||||||
# bitcoin core #
|
# bitcoin core #
|
||||||
|
@ -415,19 +407,13 @@ bitcoin_tx_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
|
||||||
# zerocash protocol primitives #
|
# zerocash protocol primitives #
|
||||||
libzerocash_a_SOURCES = \
|
libzerocash_a_SOURCES = \
|
||||||
zcash/IncrementalMerkleTree.cpp \
|
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 \
|
zerocash/utils/util.cpp \
|
||||||
zcash/NoteEncryption.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)
|
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
|
DISTCLEANFILES = obj/build.h
|
||||||
|
|
||||||
EXTRA_DIST = leveldb libzerocash/Makefile
|
EXTRA_DIST = leveldb
|
||||||
|
|
||||||
clean-local:
|
clean-local:
|
||||||
-$(MAKE) -C leveldb clean
|
-$(MAKE) -C leveldb clean
|
||||||
|
|
|
@ -6,8 +6,10 @@ zcash_gtest_SOURCES = \
|
||||||
gtest/main.cpp \
|
gtest/main.cpp \
|
||||||
gtest/test_tautology.cpp \
|
gtest/test_tautology.cpp \
|
||||||
gtest/test_checktransaction.cpp \
|
gtest/test_checktransaction.cpp \
|
||||||
|
gtest/test_joinsplit.cpp \
|
||||||
gtest/test_noteencryption.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) \
|
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)
|
$(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1)
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
bin_PROGRAMS += \
|
bin_PROGRAMS += \
|
||||||
zerocash/GenerateParamsForFiles \
|
zcash/GenerateParams \
|
||||||
zerocash/tests/utilTest \
|
zerocash/tests/utilTest
|
||||||
zerocash/tests/zerocashTest \
|
|
||||||
zerocash/tests/test_zerocash_pour_ppzksnark
|
|
||||||
|
|
||||||
# tool for generating our public parameters
|
# tool for generating our public parameters
|
||||||
zerocash_GenerateParamsForFiles_SOURCES = zerocash/GenerateParamsForFiles.cpp
|
zcash_GenerateParams_SOURCES = zcash/GenerateParams.cpp
|
||||||
zerocash_GenerateParamsForFiles_LDADD = \
|
zcash_GenerateParams_LDADD = \
|
||||||
$(BOOST_LIBS) \
|
$(BOOST_LIBS) \
|
||||||
$(LIBZEROCASH) \
|
$(LIBZEROCASH) \
|
||||||
|
$(LIBBITCOIN_UTIL) \
|
||||||
$(LIBBITCOIN_CRYPTO) \
|
$(LIBBITCOIN_CRYPTO) \
|
||||||
$(LIBZEROCASH_LIBS)
|
$(LIBZEROCASH_LIBS)
|
||||||
|
|
||||||
|
@ -20,26 +19,3 @@ zerocash_tests_utilTest_LDADD = \
|
||||||
$(LIBBITCOIN_UTIL) \
|
$(LIBBITCOIN_UTIL) \
|
||||||
$(LIBBITCOIN_CRYPTO) \
|
$(LIBBITCOIN_CRYPTO) \
|
||||||
$(LIBZEROCASH_LIBS)
|
$(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)
|
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -40,7 +40,6 @@ read_json(const std::string& jsondata)
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "zcash/IncrementalMerkleTree.hpp"
|
#include "zcash/IncrementalMerkleTree.hpp"
|
||||||
#include "zerocash/IncrementalMerkleTree.h"
|
|
||||||
#include "zerocash/utils/util.h"
|
#include "zerocash/utils/util.h"
|
||||||
|
|
||||||
//#define PRINT_JSON 1
|
//#define PRINT_JSON 1
|
||||||
|
@ -106,107 +105,11 @@ void expect_test_vector(T& it, const U& expected)
|
||||||
#endif
|
#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>
|
template<typename A, typename B, typename C>
|
||||||
void expect_ser_test_vector(B& b, const C& c, const A& tree) {
|
void expect_ser_test_vector(B& b, const C& c, const A& tree) {
|
||||||
expect_test_vector<B, C>(b, c);
|
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>
|
template<typename Tree, typename Witness>
|
||||||
void test_tree(Array root_tests, Array ser_tests, Array witness_ser_tests, Array path_tests) {
|
void test_tree(Array root_tests, Array ser_tests, Array witness_ser_tests, Array path_tests) {
|
||||||
Array::iterator root_iterator = root_tests.begin();
|
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 {
|
} else {
|
||||||
auto path = wit.path();
|
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);
|
expect_test_vector(path_iterator, path);
|
||||||
|
|
||||||
typedef Fr<default_r1cs_ppzksnark_pp> FieldT;
|
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
|
// Tree should be full now
|
||||||
ASSERT_THROW(tree.append(uint256()), std::runtime_error);
|
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)));
|
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<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) {
|
TEST(merkletree, deserializeInvalid) {
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
#include "zcash/NoteEncryption.hpp"
|
#include "zcash/NoteEncryption.hpp"
|
||||||
#include "zcash/prf.h"
|
#include "zcash/prf.h"
|
||||||
|
#include "crypto/sha256.h"
|
||||||
|
|
||||||
class TestNoteDecryption : public ZCNoteDecryption {
|
class TestNoteDecryption : public ZCNoteDecryption {
|
||||||
public:
|
public:
|
||||||
|
@ -28,8 +29,8 @@ TEST(noteencryption, api)
|
||||||
ASSERT_TRUE(b.get_epk() != c.get_epk());
|
ASSERT_TRUE(b.get_epk() != c.get_epk());
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::array<unsigned char, 216> message;
|
boost::array<unsigned char, 201> message;
|
||||||
for (unsigned char i = 0; i < 216; i++) {
|
for (unsigned char i = 0; i < 201; i++) {
|
||||||
// Fill the message with dummy data
|
// Fill the message with dummy data
|
||||||
message[i] = (unsigned char) i;
|
message[i] = (unsigned char) i;
|
||||||
}
|
}
|
||||||
|
|
22
src/init.cpp
22
src/init.cpp
|
@ -52,7 +52,7 @@
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
libzerocash::ZerocashParams *pzerocashParams = NULL;
|
ZCJoinSplit* pzcashParams = NULL;
|
||||||
|
|
||||||
#ifdef ENABLE_WALLET
|
#ifdef ENABLE_WALLET
|
||||||
CWallet* pwalletMain = NULL;
|
CWallet* pwalletMain = NULL;
|
||||||
|
@ -602,27 +602,21 @@ static void ZC_LoadParams()
|
||||||
struct timeval tv_start, tv_end;
|
struct timeval tv_start, tv_end;
|
||||||
float elapsed;
|
float elapsed;
|
||||||
|
|
||||||
boost::filesystem::path pk_path = ZC_GetParamsDir() / "zc-testnet-public-alpha-proving.key";
|
boost::filesystem::path pk_path = ZC_GetParamsDir() / "z3-proving.key";
|
||||||
boost::filesystem::path vk_path = ZC_GetParamsDir() / "zc-testnet-public-alpha-verification.key";
|
boost::filesystem::path vk_path = ZC_GetParamsDir() / "z3-verification.key";
|
||||||
|
|
||||||
libzerocash::ZerocashParams::zerocash_pp::init_public_params();
|
|
||||||
|
|
||||||
|
pzcashParams = ZCJoinSplit::Unopened();
|
||||||
|
|
||||||
LogPrintf("Loading verification key from %s\n", vk_path.string().c_str());
|
LogPrintf("Loading verification key from %s\n", vk_path.string().c_str());
|
||||||
gettimeofday(&tv_start, 0);
|
gettimeofday(&tv_start, 0);
|
||||||
auto vk_loaded = libzerocash::ZerocashParams::LoadVerificationKeyFromFile(
|
|
||||||
vk_path.string(),
|
pzcashParams->loadVerifyingKey(vk_path.string());
|
||||||
INCREMENTAL_MERKLE_TREE_DEPTH
|
|
||||||
);
|
|
||||||
gettimeofday(&tv_end, 0);
|
gettimeofday(&tv_end, 0);
|
||||||
elapsed = float(tv_end.tv_sec-tv_start.tv_sec) + (tv_end.tv_usec-tv_start.tv_usec)/float(1000000);
|
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);
|
LogPrintf("Loaded verification key in %fs seconds.\n", elapsed);
|
||||||
|
|
||||||
pzerocashParams = new libzerocash::ZerocashParams(
|
pzcashParams->setProvingKeyPath(pk_path.string());
|
||||||
INCREMENTAL_MERKLE_TREE_DEPTH,
|
|
||||||
pk_path.string(),
|
|
||||||
&vk_loaded
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Initialize bitcoin.
|
/** Initialize bitcoin.
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "zerocash/ZerocashParams.h"
|
#include "zcash/JoinSplit.hpp"
|
||||||
|
|
||||||
class CScheduler;
|
class CScheduler;
|
||||||
class CWallet;
|
class CWallet;
|
||||||
|
@ -19,7 +19,7 @@ class thread_group;
|
||||||
} // namespace boost
|
} // namespace boost
|
||||||
|
|
||||||
extern CWallet* pwalletMain;
|
extern CWallet* pwalletMain;
|
||||||
extern libzerocash::ZerocashParams* pzerocashParams;
|
extern ZCJoinSplit* pzcashParams;
|
||||||
|
|
||||||
void StartShutdown();
|
void StartShutdown();
|
||||||
bool ShutdownRequested();
|
bool ShutdownRequested();
|
||||||
|
|
|
@ -960,7 +960,10 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
|
||||||
|
|
||||||
if (state.PerformPourVerification()) {
|
if (state.PerformPourVerification()) {
|
||||||
BOOST_FOREACH(const CPourTx &pour, tx.vpour) {
|
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"),
|
return state.DoS(100, error("CheckTransaction(): pour does not verify"),
|
||||||
REJECT_INVALID, "bad-txns-pour-verification-failed");
|
REJECT_INVALID, "bad-txns-pour-verification-failed");
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,84 +9,56 @@
|
||||||
#include "tinyformat.h"
|
#include "tinyformat.h"
|
||||||
#include "utilstrencodings.h"
|
#include "utilstrencodings.h"
|
||||||
|
|
||||||
#include "zerocash/PourProver.h"
|
CPourTx::CPourTx(ZCJoinSplit& params,
|
||||||
#include "zerocash/PourTransaction.h"
|
const uint256& pubKeyHash,
|
||||||
|
|
||||||
template<std::size_t N>
|
|
||||||
boost::array<std::vector<unsigned char>, N> uint256_to_array(const boost::array<uint256, N>& in) {
|
|
||||||
boost::array<std::vector<unsigned char>, N> result;
|
|
||||||
for (size_t i = 0; i < N; i++) {
|
|
||||||
result[i] = std::vector<unsigned char>(in[i].begin(), in[i].end());
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<std::size_t N>
|
|
||||||
boost::array<uint256, N> unsigned_char_vector_array_to_uint256_array(const boost::array<std::vector<unsigned char>, N>& in) {
|
|
||||||
boost::array<uint256, N> result;
|
|
||||||
for (size_t i = 0; i < N; i++) {
|
|
||||||
result[i] = uint256(in[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
CPourTx::CPourTx(ZerocashParams& params,
|
|
||||||
const CScript& scriptPubKey,
|
|
||||||
const uint256& anchor,
|
const uint256& anchor,
|
||||||
const boost::array<PourInput, NUM_POUR_INPUTS>& inputs,
|
const boost::array<libzcash::JSInput, ZC_NUM_JS_INPUTS>& inputs,
|
||||||
const boost::array<PourOutput, NUM_POUR_OUTPUTS>& outputs,
|
const boost::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS>& outputs,
|
||||||
CAmount vpub_old,
|
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;
|
boost::array<libzcash::Note, ZC_NUM_JS_OUTPUTS> notes;
|
||||||
{
|
|
||||||
CHashWriter ss(SER_GETHASH, 0);
|
|
||||||
ss << scriptPubKey;
|
|
||||||
scriptPubKeyHash = ss.GetHash();
|
|
||||||
}
|
|
||||||
|
|
||||||
PourTransaction pourtx(params,
|
params.loadProvingKey();
|
||||||
std::vector<unsigned char>(scriptPubKeyHash.begin(), scriptPubKeyHash.end()),
|
proof = params.prove(
|
||||||
std::vector<unsigned char>(anchor.begin(), anchor.end()),
|
inputs,
|
||||||
std::vector<PourInput>(inputs.begin(), inputs.end()),
|
outputs,
|
||||||
std::vector<PourOutput>(outputs.begin(), outputs.end()),
|
notes,
|
||||||
vpub_old,
|
ciphertexts,
|
||||||
vpub_new);
|
ephemeralKey,
|
||||||
|
pubKeyHash,
|
||||||
boost::array<std::vector<unsigned char>, NUM_POUR_INPUTS> serials_bv;
|
randomSeed,
|
||||||
boost::array<std::vector<unsigned char>, NUM_POUR_OUTPUTS> commitments_bv;
|
macs,
|
||||||
boost::array<std::vector<unsigned char>, NUM_POUR_INPUTS> macs_bv;
|
serials,
|
||||||
|
commitments,
|
||||||
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()),
|
|
||||||
vpub_old,
|
vpub_old,
|
||||||
vpub_new,
|
vpub_new,
|
||||||
uint256_to_array<NUM_POUR_INPUTS>(serials),
|
anchor
|
||||||
uint256_to_array<NUM_POUR_OUTPUTS>(commitments),
|
|
||||||
uint256_to_array<NUM_POUR_INPUTS>(macs),
|
|
||||||
proof
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
std::string COutPoint::ToString() const
|
||||||
{
|
{
|
||||||
return strprintf("COutPoint(%s, %u)", hash.ToString().substr(0,10), n);
|
return strprintf("COutPoint(%s, %u)", hash.ToString().substr(0,10), n);
|
||||||
|
|
|
@ -13,16 +13,9 @@
|
||||||
|
|
||||||
#include <boost/array.hpp>
|
#include <boost/array.hpp>
|
||||||
|
|
||||||
#include "zerocash/ZerocashParams.h"
|
|
||||||
#include "zerocash/PourInput.h"
|
|
||||||
#include "zerocash/PourOutput.h"
|
|
||||||
|
|
||||||
#include "zcash/NoteEncryption.hpp"
|
#include "zcash/NoteEncryption.hpp"
|
||||||
|
#include "zcash/Zcash.h"
|
||||||
using namespace libzerocash;
|
#include "zcash/JoinSplit.hpp"
|
||||||
|
|
||||||
static const unsigned int NUM_POUR_INPUTS = 2;
|
|
||||||
static const unsigned int NUM_POUR_OUTPUTS = 2;
|
|
||||||
|
|
||||||
class CPourTx
|
class CPourTx
|
||||||
{
|
{
|
||||||
|
@ -32,14 +25,6 @@ public:
|
||||||
CAmount vpub_old;
|
CAmount vpub_old;
|
||||||
CAmount vpub_new;
|
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
|
// Pours are always anchored to a root in the bucket
|
||||||
// commitment tree at some point in the blockchain
|
// commitment tree at some point in the blockchain
|
||||||
// history or in the history of the current
|
// history or in the history of the current
|
||||||
|
@ -50,28 +35,31 @@ public:
|
||||||
// are derived from the secrets placed in the bucket
|
// are derived from the secrets placed in the bucket
|
||||||
// and the secret spend-authority key known by the
|
// and the secret spend-authority key known by the
|
||||||
// spender.
|
// spender.
|
||||||
boost::array<uint256, NUM_POUR_INPUTS> serials;
|
boost::array<uint256, ZC_NUM_JS_INPUTS> serials;
|
||||||
|
|
||||||
// Bucket commitments are introduced into the commitment
|
// Bucket commitments are introduced into the commitment
|
||||||
// tree, blinding the public about the values and
|
// tree, blinding the public about the values and
|
||||||
// destinations involved in the Pour. The presence of a
|
// destinations involved in the Pour. The presence of a
|
||||||
// commitment in the bucket commitment tree is required
|
// commitment in the bucket commitment tree is required
|
||||||
// to spend it.
|
// to spend it.
|
||||||
boost::array<uint256, NUM_POUR_OUTPUTS> commitments;
|
boost::array<uint256, ZC_NUM_JS_OUTPUTS> commitments;
|
||||||
|
|
||||||
// Ciphertexts
|
// Ciphertexts
|
||||||
// These contain trapdoors, values and other information
|
// These contain trapdoors, values and other information
|
||||||
// that the recipient needs, including a memo field. It
|
// that the recipient needs, including a memo field. It
|
||||||
// is encrypted using the scheme implemented in crypto/NoteEncryption.cpp
|
// 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
|
// Ephemeral key
|
||||||
uint256 ephemeralKey;
|
uint256 ephemeralKey;
|
||||||
|
|
||||||
|
// Random seed
|
||||||
|
uint256 randomSeed;
|
||||||
|
|
||||||
// MACs
|
// MACs
|
||||||
// The verification of the pour requires these MACs
|
// The verification of the pour requires these MACs
|
||||||
// to be provided as an input.
|
// to be provided as an input.
|
||||||
boost::array<uint256, NUM_POUR_INPUTS> macs;
|
boost::array<uint256, ZC_NUM_JS_INPUTS> macs;
|
||||||
|
|
||||||
// Pour proof
|
// Pour proof
|
||||||
// This is a zk-SNARK which ensures that this pour is valid.
|
// This is a zk-SNARK which ensures that this pour is valid.
|
||||||
|
@ -79,17 +67,20 @@ public:
|
||||||
|
|
||||||
CPourTx(): vpub_old(0), vpub_new(0) { }
|
CPourTx(): vpub_old(0), vpub_new(0) { }
|
||||||
|
|
||||||
CPourTx(ZerocashParams& params,
|
CPourTx(ZCJoinSplit& params,
|
||||||
const CScript& scriptPubKey,
|
const uint256& pubKeyHash,
|
||||||
const uint256& rt,
|
const uint256& rt,
|
||||||
const boost::array<PourInput, NUM_POUR_INPUTS>& inputs,
|
const boost::array<libzcash::JSInput, ZC_NUM_JS_INPUTS>& inputs,
|
||||||
const boost::array<PourOutput, NUM_POUR_OUTPUTS>& outputs,
|
const boost::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS>& outputs,
|
||||||
CAmount vpub_old,
|
CAmount vpub_old,
|
||||||
CAmount vpub_new
|
CAmount vpub_new
|
||||||
);
|
);
|
||||||
|
|
||||||
// Verifies that the pour proof is correct.
|
// 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;
|
ADD_SERIALIZE_METHODS;
|
||||||
|
|
||||||
|
@ -97,13 +88,12 @@ public:
|
||||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||||
READWRITE(vpub_old);
|
READWRITE(vpub_old);
|
||||||
READWRITE(vpub_new);
|
READWRITE(vpub_new);
|
||||||
READWRITE(scriptPubKey);
|
|
||||||
READWRITE(scriptSig);
|
|
||||||
READWRITE(anchor);
|
READWRITE(anchor);
|
||||||
READWRITE(serials);
|
READWRITE(serials);
|
||||||
READWRITE(commitments);
|
READWRITE(commitments);
|
||||||
READWRITE(ciphertexts);
|
READWRITE(ciphertexts);
|
||||||
READWRITE(ephemeralKey);
|
READWRITE(ephemeralKey);
|
||||||
|
READWRITE(randomSeed);
|
||||||
READWRITE(macs);
|
READWRITE(macs);
|
||||||
READWRITE(proof);
|
READWRITE(proof);
|
||||||
}
|
}
|
||||||
|
@ -113,13 +103,12 @@ public:
|
||||||
return (
|
return (
|
||||||
a.vpub_old == b.vpub_old &&
|
a.vpub_old == b.vpub_old &&
|
||||||
a.vpub_new == b.vpub_new &&
|
a.vpub_new == b.vpub_new &&
|
||||||
a.scriptPubKey == b.scriptPubKey &&
|
|
||||||
a.scriptSig == b.scriptSig &&
|
|
||||||
a.anchor == b.anchor &&
|
a.anchor == b.anchor &&
|
||||||
a.serials == b.serials &&
|
a.serials == b.serials &&
|
||||||
a.commitments == b.commitments &&
|
a.commitments == b.commitments &&
|
||||||
a.ciphertexts == b.ciphertexts &&
|
a.ciphertexts == b.ciphertexts &&
|
||||||
a.ephemeralKey == b.ephemeralKey &&
|
a.ephemeralKey == b.ephemeralKey &&
|
||||||
|
a.randomSeed == b.randomSeed &&
|
||||||
a.macs == b.macs &&
|
a.macs == b.macs &&
|
||||||
a.proof == b.proof
|
a.proof == b.proof
|
||||||
);
|
);
|
||||||
|
|
|
@ -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_old", ValueFromAmount(pourtx.vpub_old)));
|
||||||
pour.push_back(Pair("vpub_new", ValueFromAmount(pourtx.vpub_new)));
|
pour.push_back(Pair("vpub_new", ValueFromAmount(pourtx.vpub_new)));
|
||||||
|
|
||||||
{
|
// TODO: #808
|
||||||
Object o;
|
uint256 pubKeyHash;
|
||||||
ScriptPubKeyToJSON(pourtx.scriptPubKey, o, true);
|
pour.push_back(Pair("valid", pourtx.Verify(*pzcashParams, pubKeyHash)));
|
||||||
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)));
|
|
||||||
|
|
||||||
vpour.push_back(pour);
|
vpour.push_back(pour);
|
||||||
}
|
}
|
||||||
|
|
|
@ -166,10 +166,12 @@ BOOST_AUTO_TEST_CASE(serials_test)
|
||||||
|
|
||||||
void appendRandomCommitment(ZCIncrementalMerkleTree &tree)
|
void appendRandomCommitment(ZCIncrementalMerkleTree &tree)
|
||||||
{
|
{
|
||||||
Address addr = Address::CreateNewRandomAddress();
|
libzcash::SpendingKey k = libzcash::SpendingKey::random();
|
||||||
Coin coin(addr.getPublicAddress(), 100);
|
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)
|
BOOST_AUTO_TEST_CASE(anchors_flush_test)
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -124,12 +124,12 @@ void static RandomTransaction(CMutableTransaction &tx, bool fSingle) {
|
||||||
} else {
|
} else {
|
||||||
pourtx.vpub_new = insecure_rand() % 100000000;
|
pourtx.vpub_new = insecure_rand() % 100000000;
|
||||||
}
|
}
|
||||||
RandomScript(pourtx.scriptPubKey);
|
|
||||||
RandomScript(pourtx.scriptSig);
|
|
||||||
pourtx.anchor = GetRandHash();
|
pourtx.anchor = GetRandHash();
|
||||||
pourtx.serials[0] = GetRandHash();
|
pourtx.serials[0] = GetRandHash();
|
||||||
pourtx.serials[1] = GetRandHash();
|
pourtx.serials[1] = GetRandHash();
|
||||||
pourtx.ephemeralKey = GetRandHash();
|
pourtx.ephemeralKey = GetRandHash();
|
||||||
|
pourtx.randomSeed = GetRandHash();
|
||||||
pourtx.ciphertexts[0] = {insecure_rand() % 100, insecure_rand() % 100};
|
pourtx.ciphertexts[0] = {insecure_rand() % 100, insecure_rand() % 100};
|
||||||
pourtx.ciphertexts[1] = {insecure_rand() % 100, insecure_rand() % 100};
|
pourtx.ciphertexts[1] = {insecure_rand() % 100, insecure_rand() % 100};
|
||||||
pourtx.macs[0] = GetRandHash();
|
pourtx.macs[0] = GetRandHash();
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
|
|
||||||
CClientUIInterface uiInterface; // Declared but not defined in ui_interface.h
|
CClientUIInterface uiInterface; // Declared but not defined in ui_interface.h
|
||||||
CWallet* pwalletMain;
|
CWallet* pwalletMain;
|
||||||
libzerocash::ZerocashParams *pzerocashParams;
|
ZCJoinSplit *pzcashParams;
|
||||||
|
|
||||||
extern bool fPrintToConsole;
|
extern bool fPrintToConsole;
|
||||||
extern void noui_connect();
|
extern void noui_connect();
|
||||||
|
|
|
@ -25,15 +25,11 @@
|
||||||
#include <boost/assign/list_of.hpp>
|
#include <boost/assign/list_of.hpp>
|
||||||
#include "json/json_spirit_writer_template.h"
|
#include "json/json_spirit_writer_template.h"
|
||||||
|
|
||||||
#include "zerocash/ZerocashParams.h"
|
#include "zcash/Note.hpp"
|
||||||
#include "zerocash/PourInput.h"
|
#include "zcash/Address.hpp"
|
||||||
#include "zerocash/PourOutput.h"
|
|
||||||
#include "zerocash/Address.h"
|
|
||||||
#include "zerocash/Coin.h"
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace json_spirit;
|
using namespace json_spirit;
|
||||||
using namespace libzerocash;
|
|
||||||
|
|
||||||
// In script_tests.cpp
|
// In script_tests.cpp
|
||||||
extern Array read_json(const std::string& jsondata);
|
extern Array read_json(const std::string& jsondata);
|
||||||
|
@ -311,19 +307,18 @@ BOOST_AUTO_TEST_CASE(test_basic_pour_verification)
|
||||||
// the integrity of the scheme through its own tests.
|
// the integrity of the scheme through its own tests.
|
||||||
|
|
||||||
// construct the r1cs keypair
|
// construct the r1cs keypair
|
||||||
auto keypair = ZerocashParams::GenerateNewKeyPair(INCREMENTAL_MERKLE_TREE_DEPTH);
|
auto p = ZCJoinSplit::Generate();
|
||||||
ZerocashParams p(
|
|
||||||
INCREMENTAL_MERKLE_TREE_DEPTH,
|
|
||||||
&keypair
|
|
||||||
);
|
|
||||||
|
|
||||||
// construct a merkle tree
|
// construct a merkle tree
|
||||||
ZCIncrementalMerkleTree merkleTree;
|
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
|
// commitment from coin
|
||||||
uint256 commitment(coin.getCoinCommitment().getCommitmentValue());
|
uint256 commitment = note.cm();
|
||||||
|
|
||||||
// insert commitment into the merkle tree
|
// insert commitment into the merkle tree
|
||||||
merkleTree.append(commitment);
|
merkleTree.append(commitment);
|
||||||
|
@ -332,22 +327,21 @@ BOOST_AUTO_TEST_CASE(test_basic_pour_verification)
|
||||||
uint256 rt = merkleTree.root();
|
uint256 rt = merkleTree.root();
|
||||||
|
|
||||||
auto witness = merkleTree.witness();
|
auto witness = merkleTree.witness();
|
||||||
auto path = witness.path();
|
|
||||||
|
|
||||||
// create CPourTx
|
// create CPourTx
|
||||||
CScript scriptPubKey;
|
uint256 pubKeyHash;
|
||||||
boost::array<PourInput, NUM_POUR_INPUTS> inputs = {
|
boost::array<libzcash::JSInput, ZC_NUM_JS_INPUTS> inputs = {
|
||||||
PourInput(coin, addr, path),
|
libzcash::JSInput(witness, note, k),
|
||||||
PourInput(INCREMENTAL_MERKLE_TREE_DEPTH) // dummy input of zero value
|
libzcash::JSInput() // dummy input of zero value
|
||||||
};
|
};
|
||||||
boost::array<PourOutput, NUM_POUR_OUTPUTS> outputs = {
|
boost::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS> outputs = {
|
||||||
PourOutput(50),
|
libzcash::JSOutput(addr, 50),
|
||||||
PourOutput(50)
|
libzcash::JSOutput(addr, 50)
|
||||||
};
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
CPourTx pourtx(p, scriptPubKey, uint256(rt), inputs, outputs, 0, 0);
|
CPourTx pourtx(*p, pubKeyHash, rt, inputs, outputs, 0, 0);
|
||||||
BOOST_CHECK(pourtx.Verify(p));
|
BOOST_CHECK(pourtx.Verify(*p, pubKeyHash));
|
||||||
|
|
||||||
CDataStream ss(SER_DISK, CLIENT_VERSION);
|
CDataStream ss(SER_DISK, CLIENT_VERSION);
|
||||||
ss << pourtx;
|
ss << pourtx;
|
||||||
|
@ -356,21 +350,23 @@ BOOST_AUTO_TEST_CASE(test_basic_pour_verification)
|
||||||
ss >> pourtx_deserialized;
|
ss >> pourtx_deserialized;
|
||||||
|
|
||||||
BOOST_CHECK(pourtx_deserialized == pourtx);
|
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.
|
// 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, pubKeyHash, 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, 0, 10), std::invalid_argument);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// Ensure that it won't verify if the root is changed.
|
// Ensure that it won't verify if the root is changed.
|
||||||
auto test = CPourTx(p, scriptPubKey, uint256(rt), inputs, outputs, 0, 0);
|
auto test = CPourTx(*p, pubKeyHash, rt, inputs, outputs, 0, 0);
|
||||||
test.anchor = GetRandHash();
|
test.anchor = GetRandHash();
|
||||||
BOOST_CHECK(!test.Verify(p));
|
BOOST_CHECK(!test.Verify(*p, pubKeyHash));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delete p;
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(test_simple_pour_invalidity)
|
BOOST_AUTO_TEST_CASE(test_simple_pour_invalidity)
|
||||||
|
|
|
@ -29,6 +29,8 @@
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace json_spirit;
|
using namespace json_spirit;
|
||||||
|
|
||||||
|
using namespace libzcash;
|
||||||
|
|
||||||
int64_t nWalletUnlockTime;
|
int64_t nWalletUnlockTime;
|
||||||
static CCriticalSection cs_nWalletUnlockTime;
|
static CCriticalSection cs_nWalletUnlockTime;
|
||||||
|
|
||||||
|
@ -2385,7 +2387,7 @@ Value zc_benchmark(const json_spirit::Array& params, bool fHelp)
|
||||||
if (benchmarktype == "createjoinsplit") {
|
if (benchmarktype == "createjoinsplit") {
|
||||||
/* Load the proving now key so that it doesn't happen as part of the
|
/* Load the proving now key so that it doesn't happen as part of the
|
||||||
* first joinsplit. */
|
* first joinsplit. */
|
||||||
pzerocashParams->loadProvingKey();
|
pzcashParams->loadProvingKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < samplecount; i++) {
|
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);
|
LOCK(cs_main);
|
||||||
|
|
||||||
uint256 a_sk;
|
SpendingKey k;
|
||||||
uint256 sk_enc;
|
|
||||||
|
|
||||||
{
|
{
|
||||||
CDataStream ssData(ParseHexV(params[0], "zcsecretkey"), SER_NETWORK, PROTOCOL_VERSION);
|
CDataStream ssData(ParseHexV(params[0], "zcsecretkey"), SER_NETWORK, PROTOCOL_VERSION);
|
||||||
try {
|
try {
|
||||||
ssData >> a_sk;
|
ssData >> k;
|
||||||
ssData >> sk_enc;
|
|
||||||
} catch(const std::exception &) {
|
} catch(const std::exception &) {
|
||||||
throw runtime_error(
|
throw runtime_error(
|
||||||
"zcsecretkey could not be decoded"
|
"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;
|
uint256 epk;
|
||||||
unsigned char nonce;
|
unsigned char nonce;
|
||||||
ZCNoteEncryption::Ciphertext ct;
|
ZCNoteEncryption::Ciphertext ct;
|
||||||
|
uint256 h_sig;
|
||||||
|
|
||||||
{
|
{
|
||||||
CDataStream ssData(ParseHexV(params[1], "encrypted_bucket"), SER_NETWORK, PROTOCOL_VERSION);
|
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 >> nonce;
|
||||||
ssData >> epk;
|
ssData >> epk;
|
||||||
ssData >> ct;
|
ssData >> ct;
|
||||||
|
ssData >> h_sig;
|
||||||
} catch(const std::exception &) {
|
} catch(const std::exception &) {
|
||||||
throw runtime_error(
|
throw runtime_error(
|
||||||
"encrypted_bucket could not be decoded"
|
"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();
|
NotePlaintext npt = NotePlaintext::decrypt(
|
||||||
uint256 commitment = uint256(commitment_v);
|
decryptor,
|
||||||
|
ct,
|
||||||
|
epk,
|
||||||
|
h_sig,
|
||||||
|
nonce
|
||||||
|
);
|
||||||
|
PaymentAddress payment_addr = k.address();
|
||||||
|
Note decrypted_note = npt.note(payment_addr);
|
||||||
|
|
||||||
assert(pwalletMain != NULL);
|
assert(pwalletMain != NULL);
|
||||||
libzcash::MerklePath path;
|
std::vector<boost::optional<ZCIncrementalWitness>> witnesses;
|
||||||
uint256 anchor;
|
uint256 anchor;
|
||||||
auto found_in_chain = pwalletMain->WitnessBucketCommitment(commitment, path, anchor);
|
uint256 commitment = decrypted_note.cm();
|
||||||
|
pwalletMain->WitnessBucketCommitment(
|
||||||
CAmount value_of_bucket = decrypted_bucket.getValue();
|
{commitment},
|
||||||
|
witnesses,
|
||||||
|
anchor
|
||||||
|
);
|
||||||
|
|
||||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||||
{
|
ss << npt;
|
||||||
ss << decrypted_bucket.getValue();
|
|
||||||
ss << decrypted_bucket.getRho();
|
|
||||||
ss << decrypted_bucket.getR();
|
|
||||||
}
|
|
||||||
|
|
||||||
Object result;
|
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("bucket", HexStr(ss.begin(), ss.end())));
|
||||||
result.push_back(Pair("exists", found_in_chain));
|
result.push_back(Pair("exists", (bool) witnesses[0]));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Value zc_raw_pour(const json_spirit::Array& params, bool fHelp)
|
Value zc_raw_pour(const json_spirit::Array& params, bool fHelp)
|
||||||
{
|
{
|
||||||
if (!EnsureWalletIsAvailable(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)
|
if (params[4].get_real() != 0.0)
|
||||||
vpub_new = AmountFromValue(params[4]);
|
vpub_new = AmountFromValue(params[4]);
|
||||||
|
|
||||||
std::vector<PourInput> vpourin;
|
std::vector<JSInput> vpourin;
|
||||||
std::vector<PourOutput> vpourout;
|
std::vector<JSOutput> vpourout;
|
||||||
|
std::vector<Note> notes;
|
||||||
uint256 anchor;
|
std::vector<SpendingKey> keys;
|
||||||
|
std::vector<uint256> commitments;
|
||||||
|
|
||||||
BOOST_FOREACH(const Pair& s, inputs)
|
BOOST_FOREACH(const Pair& s, inputs)
|
||||||
{
|
{
|
||||||
CDataStream ssData(ParseHexV(s.name_, "bucket"), SER_NETWORK, PROTOCOL_VERSION);
|
SpendingKey k;
|
||||||
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;
|
|
||||||
|
|
||||||
{
|
{
|
||||||
CDataStream ssData2(ParseHexV(s.value_, "zcsecretkey"), SER_NETWORK, PROTOCOL_VERSION);
|
CDataStream ssData(ParseHexV(s.value_, "zcsecretkey"), SER_NETWORK, PROTOCOL_VERSION);
|
||||||
try {
|
try {
|
||||||
ssData2 >> a_sk;
|
ssData >> k;
|
||||||
ssData2 >> sk_enc;
|
|
||||||
} catch(const std::exception &) {
|
} catch(const std::exception &) {
|
||||||
throw runtime_error(
|
throw runtime_error(
|
||||||
"zcsecretkey could not be decoded"
|
"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);
|
keys.push_back(k);
|
||||||
libzerocash::Address zcaddress(zcsecretkey);
|
|
||||||
libzerocash::Coin input_coin(zcaddress.getPublicAddress(), value, rho, r);
|
|
||||||
|
|
||||||
std::vector<unsigned char> commitment_v = input_coin.getCoinCommitment().getCommitmentValue();
|
NotePlaintext npt;
|
||||||
uint256 commitment = uint256(commitment_v);
|
|
||||||
|
|
||||||
libzcash::MerklePath path;
|
{
|
||||||
assert(pwalletMain != NULL);
|
CDataStream ssData(ParseHexV(s.name_, "bucket"), SER_NETWORK, PROTOCOL_VERSION);
|
||||||
if (!pwalletMain->WitnessBucketCommitment(commitment, path, anchor)) {
|
ssData >> npt;
|
||||||
throw std::runtime_error("Couldn't find bucket in the blockchain");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
uint256 anchor;
|
||||||
vpourin.push_back(PourInput(INCREMENTAL_MERKLE_TREE_DEPTH));
|
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)
|
BOOST_FOREACH(const Pair& s, outputs)
|
||||||
{
|
{
|
||||||
libzerocash::PublicAddress addrTo;
|
PaymentAddress addrTo;
|
||||||
|
|
||||||
{
|
{
|
||||||
CDataStream ssData(ParseHexV(s.name_, "to_address"), SER_NETWORK, PROTOCOL_VERSION);
|
CDataStream ssData(ParseHexV(s.name_, "to_address"), SER_NETWORK, PROTOCOL_VERSION);
|
||||||
|
ssData >> addrTo;
|
||||||
uint256 pubAddressSecret;
|
|
||||||
uint256 encryptionPublicKey;
|
|
||||||
|
|
||||||
ssData >> pubAddressSecret;
|
|
||||||
ssData >> encryptionPublicKey;
|
|
||||||
|
|
||||||
addrTo = libzerocash::PublicAddress(pubAddressSecret, encryptionPublicKey);
|
|
||||||
}
|
}
|
||||||
CAmount nAmount = AmountFromValue(s.value_);
|
CAmount nAmount = AmountFromValue(s.value_);
|
||||||
|
|
||||||
libzerocash::Coin coin(addrTo, nAmount);
|
vpourout.push_back(JSOutput(addrTo, nAmount));
|
||||||
libzerocash::PourOutput output(coin, addrTo);
|
|
||||||
|
|
||||||
vpourout.push_back(output);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while (vpourout.size() < NUM_POUR_OUTPUTS) {
|
while (vpourout.size() < ZC_NUM_JS_OUTPUTS) {
|
||||||
vpourout.push_back(PourOutput(0));
|
vpourout.push_back(JSOutput());
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
// 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");
|
throw runtime_error("unsupported pour input/output counts");
|
||||||
}
|
}
|
||||||
|
|
||||||
CScript scriptPubKey;
|
// TODO: #808
|
||||||
CPourTx pourtx(*pzerocashParams,
|
uint256 pubKeyHash;
|
||||||
scriptPubKey,
|
CPourTx pourtx(*pzcashParams,
|
||||||
|
pubKeyHash,
|
||||||
anchor,
|
anchor,
|
||||||
{vpourin[0], vpourin[1]},
|
{vpourin[0], vpourin[1]},
|
||||||
{vpourout[0], vpourout[1]},
|
{vpourout[0], vpourout[1]},
|
||||||
vpub_old,
|
vpub_old,
|
||||||
vpub_new);
|
vpub_new);
|
||||||
|
|
||||||
assert(pourtx.Verify(*pzerocashParams));
|
assert(pourtx.Verify(*pzcashParams, pubKeyHash));
|
||||||
|
|
||||||
CMutableTransaction mtx(tx);
|
CMutableTransaction mtx(tx);
|
||||||
mtx.nVersion = 2;
|
mtx.nVersion = 2;
|
||||||
|
@ -2673,6 +2678,7 @@ Value zc_raw_pour(const json_spirit::Array& params, bool fHelp)
|
||||||
ss2 << ((unsigned char) 0x00);
|
ss2 << ((unsigned char) 0x00);
|
||||||
ss2 << pourtx.ephemeralKey;
|
ss2 << pourtx.ephemeralKey;
|
||||||
ss2 << pourtx.ciphertexts[0];
|
ss2 << pourtx.ciphertexts[0];
|
||||||
|
ss2 << pourtx.h_sig(*pzcashParams, pubKeyHash);
|
||||||
|
|
||||||
encryptedBucket1 = HexStr(ss2.begin(), ss2.end());
|
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 << ((unsigned char) 0x01);
|
||||||
ss2 << pourtx.ephemeralKey;
|
ss2 << pourtx.ephemeralKey;
|
||||||
ss2 << pourtx.ciphertexts[1];
|
ss2 << pourtx.ciphertexts[1];
|
||||||
|
ss2 << pourtx.h_sig(*pzcashParams, pubKeyHash);
|
||||||
|
|
||||||
encryptedBucket2 = HexStr(ss2.begin(), ss2.end());
|
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 pub(SER_NETWORK, PROTOCOL_VERSION);
|
||||||
CDataStream priv(SER_NETWORK, PROTOCOL_VERSION);
|
CDataStream priv(SER_NETWORK, PROTOCOL_VERSION);
|
||||||
|
CDataStream viewing(SER_NETWORK, PROTOCOL_VERSION);
|
||||||
|
|
||||||
pub << zckeypair.getPublicAddress().getPublicAddressSecret(); // a_pk
|
pub << addr;
|
||||||
pub << zckeypair.getPublicAddress().getEncryptionPublicKey(); // pk_enc
|
priv << k;
|
||||||
|
viewing << viewing_key;
|
||||||
priv << zckeypair.getPrivateAddress().getAddressSecret(); // a_sk
|
|
||||||
priv << zckeypair.getPrivateAddress().getEncryptionSecretKey(); // sk_enc
|
|
||||||
|
|
||||||
std::string pub_hex = HexStr(pub.begin(), pub.end());
|
std::string pub_hex = HexStr(pub.begin(), pub.end());
|
||||||
std::string priv_hex = HexStr(priv.begin(), priv.end());
|
std::string priv_hex = HexStr(priv.begin(), priv.end());
|
||||||
|
std::string viewing_hex = HexStr(viewing.begin(), viewing.end());
|
||||||
|
|
||||||
Object result;
|
Object result;
|
||||||
result.push_back(Pair("zcaddress", pub_hex));
|
result.push_back(Pair("zcaddress", pub_hex));
|
||||||
result.push_back(Pair("zcsecretkey", priv_hex));
|
result.push_back(Pair("zcsecretkey", priv_hex));
|
||||||
|
result.push_back(Pair("zcviewingkey", viewing_hex));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1051,14 +1051,13 @@ bool CWalletTx::WriteToDisk(CWalletDB *pwalletdb)
|
||||||
return pwalletdb->WriteTx(GetHash(), *this);
|
return pwalletdb->WriteTx(GetHash(), *this);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CWallet::WitnessBucketCommitment(uint256 &commitment,
|
void CWallet::WitnessBucketCommitment(std::vector<uint256> commitments,
|
||||||
libzcash::MerklePath &path,
|
std::vector<boost::optional<ZCIncrementalWitness>>& witnesses,
|
||||||
uint256 &final_anchor)
|
uint256 &final_anchor)
|
||||||
{
|
{
|
||||||
|
witnesses.resize(commitments.size());
|
||||||
CBlockIndex* pindex = chainActive.Genesis();
|
CBlockIndex* pindex = chainActive.Genesis();
|
||||||
ZCIncrementalMerkleTree tree;
|
ZCIncrementalMerkleTree tree;
|
||||||
boost::optional<ZCIncrementalWitness> witness = boost::none;
|
|
||||||
uint256 current_anchor;
|
|
||||||
|
|
||||||
while (pindex) {
|
while (pindex) {
|
||||||
CBlock block;
|
CBlock block;
|
||||||
|
@ -1070,24 +1069,26 @@ bool CWallet::WitnessBucketCommitment(uint256 &commitment,
|
||||||
{
|
{
|
||||||
BOOST_FOREACH(const uint256 &bucket_commitment, pour.commitments)
|
BOOST_FOREACH(const uint256 &bucket_commitment, pour.commitments)
|
||||||
{
|
{
|
||||||
if (witness) {
|
tree.append(bucket_commitment);
|
||||||
witness->append(bucket_commitment);
|
|
||||||
} else {
|
|
||||||
tree.append(bucket_commitment);
|
|
||||||
|
|
||||||
if (bucket_commitment == commitment) {
|
BOOST_FOREACH(boost::optional<ZCIncrementalWitness>& wit, witnesses) {
|
||||||
witness = tree.witness();
|
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) {
|
uint256 current_anchor = tree.root();
|
||||||
current_anchor = witness->root();
|
|
||||||
} else {
|
|
||||||
current_anchor = tree.root();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Consistency check: we should be able to find the current tree
|
// Consistency check: we should be able to find the current tree
|
||||||
// in our CCoins view.
|
// in our CCoins view.
|
||||||
|
@ -1097,14 +1098,14 @@ bool CWallet::WitnessBucketCommitment(uint256 &commitment,
|
||||||
pindex = chainActive.Next(pindex);
|
pindex = chainActive.Next(pindex);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (witness) {
|
// TODO: #93; Select a root via some heuristic.
|
||||||
path = witness->path();
|
final_anchor = tree.root();
|
||||||
final_anchor = current_anchor;
|
|
||||||
|
|
||||||
return true;
|
BOOST_FOREACH(boost::optional<ZCIncrementalWitness>& wit, witnesses) {
|
||||||
|
if (wit) {
|
||||||
|
assert(final_anchor == wit->root());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -616,7 +616,10 @@ public:
|
||||||
void SyncTransaction(const CTransaction& tx, const CBlock* pblock);
|
void SyncTransaction(const CTransaction& tx, const CBlock* pblock);
|
||||||
bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate);
|
bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate);
|
||||||
void EraseFromWallet(const uint256 &hash);
|
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);
|
int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false);
|
||||||
void ReacceptWalletTransactions();
|
void ReacceptWalletTransactions();
|
||||||
void ResendWalletTransactions(int64_t nBestBlockTime);
|
void ResendWalletTransactions(int64_t nBestBlockTime);
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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_
|
|
@ -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;
|
||||||
|
}
|
|
@ -8,8 +8,7 @@
|
||||||
#include "uint256.h"
|
#include "uint256.h"
|
||||||
#include "serialize.h"
|
#include "serialize.h"
|
||||||
|
|
||||||
static const unsigned int INCREMENTAL_MERKLE_TREE_DEPTH = 20;
|
#include "Zcash.h"
|
||||||
static const unsigned int INCREMENTAL_MERKLE_TREE_DEPTH_TESTING = 4;
|
|
||||||
|
|
||||||
namespace libzcash {
|
namespace libzcash {
|
||||||
|
|
||||||
|
|
|
@ -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>;
|
||||||
|
|
||||||
|
}
|
|
@ -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_
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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_
|
|
@ -165,7 +165,7 @@ uint256 random_uint256()
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
template class NoteEncryption<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_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>;
|
||||||
|
|
||||||
}
|
}
|
|
@ -9,7 +9,7 @@ https://github.com/zcash/zips/blob/master/protocol/protocol.pdf
|
||||||
#include <boost/array.hpp>
|
#include <boost/array.hpp>
|
||||||
#include "uint256.h"
|
#include "uint256.h"
|
||||||
|
|
||||||
#include "zerocash/Zerocash.h"
|
#include "zcash/Zcash.h"
|
||||||
|
|
||||||
namespace libzcash {
|
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::NoteEncryption<ZC_NOTEPLAINTEXT_LEADING + 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::NoteDecryption<ZC_NOTEPLAINTEXT_LEADING + ZC_V_SIZE + ZC_RHO_SIZE + ZC_R_SIZE + ZC_MEMO_SIZE> ZCNoteDecryption;
|
||||||
|
|
||||||
#endif /* ZC_NOTE_ENCRYPTION_H_ */
|
#endif /* ZC_NOTE_ENCRYPTION_H_ */
|
||||||
|
|
|
@ -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_
|
|
@ -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();
|
||||||
|
}
|
||||||
|
};
|
|
@ -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());
|
||||||
|
}
|
||||||
|
};
|
|
@ -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();
|
||||||
|
}
|
||||||
|
};
|
|
@ -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();
|
||||||
|
}
|
||||||
|
};
|
|
@ -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) {}
|
||||||
|
};
|
|
@ -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()
|
||||||
|
));
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
|
@ -1,8 +1,5 @@
|
||||||
#include "zerocash/IncrementalMerkleTree.h"
|
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
#include "zerocash/ZerocashParams.h"
|
|
||||||
#include "coins.h"
|
#include "coins.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "init.h"
|
#include "init.h"
|
||||||
|
@ -15,6 +12,11 @@
|
||||||
|
|
||||||
#include "zcbenchmarks.h"
|
#include "zcbenchmarks.h"
|
||||||
|
|
||||||
|
#include "zcash/Zcash.h"
|
||||||
|
#include "zcash/IncrementalMerkleTree.hpp"
|
||||||
|
|
||||||
|
using namespace libzcash;
|
||||||
|
|
||||||
struct timeval tv_start;
|
struct timeval tv_start;
|
||||||
|
|
||||||
void timer_start()
|
void timer_start()
|
||||||
|
@ -42,64 +44,52 @@ double benchmark_sleep()
|
||||||
double benchmark_parameter_loading()
|
double benchmark_parameter_loading()
|
||||||
{
|
{
|
||||||
// FIXME: this is duplicated with the actual loading code
|
// 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 pk_path = ZC_GetParamsDir() / "z3-proving.key";
|
||||||
boost::filesystem::path vk_path = ZC_GetParamsDir() / "zc-testnet-public-alpha-verification.key";
|
boost::filesystem::path vk_path = ZC_GetParamsDir() / "z3-verification.key";
|
||||||
|
|
||||||
timer_start();
|
timer_start();
|
||||||
auto vk_loaded = libzerocash::ZerocashParams::LoadVerificationKeyFromFile(
|
|
||||||
vk_path.string(),
|
auto newParams = ZCJoinSplit::Unopened();
|
||||||
INCREMENTAL_MERKLE_TREE_DEPTH
|
|
||||||
);
|
newParams->loadVerifyingKey(vk_path.string());
|
||||||
auto pk_loaded = libzerocash::ZerocashParams::LoadProvingKeyFromFile(
|
newParams->setProvingKeyPath(pk_path.string());
|
||||||
pk_path.string(),
|
newParams->loadProvingKey();
|
||||||
INCREMENTAL_MERKLE_TREE_DEPTH
|
|
||||||
);
|
double ret = timer_stop();
|
||||||
libzerocash::ZerocashParams zerocashParams = libzerocash::ZerocashParams(
|
|
||||||
INCREMENTAL_MERKLE_TREE_DEPTH,
|
delete newParams;
|
||||||
&pk_loaded,
|
|
||||||
&vk_loaded
|
return ret;
|
||||||
);
|
|
||||||
return timer_stop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double benchmark_create_joinsplit()
|
double benchmark_create_joinsplit()
|
||||||
{
|
{
|
||||||
CScript scriptPubKey;
|
// TODO: #808
|
||||||
|
uint256 pubKeyHash;
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get the anchor of an empty commitment tree. */
|
/* Get the anchor of an empty commitment tree. */
|
||||||
IncrementalMerkleTree blank_tree(INCREMENTAL_MERKLE_TREE_DEPTH);
|
uint256 anchor = ZCIncrementalMerkleTree().root();
|
||||||
std::vector<unsigned char> newrt_v(32);
|
|
||||||
blank_tree.getRootValue(newrt_v);
|
|
||||||
uint256 anchor = uint256(newrt_v);
|
|
||||||
|
|
||||||
timer_start();
|
timer_start();
|
||||||
CPourTx pourtx(*pzerocashParams,
|
CPourTx pourtx(*pzcashParams,
|
||||||
scriptPubKey,
|
pubKeyHash,
|
||||||
anchor,
|
anchor,
|
||||||
{vpourin[0], vpourin[1]},
|
{JSInput(), JSInput()},
|
||||||
{vpourout[0], vpourout[1]},
|
{JSOutput(), JSOutput()},
|
||||||
0,
|
0,
|
||||||
0);
|
0);
|
||||||
double ret = timer_stop();
|
double ret = timer_stop();
|
||||||
assert(pourtx.Verify(*pzerocashParams));
|
|
||||||
|
assert(pourtx.Verify(*pzcashParams, pubKeyHash));
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
double benchmark_verify_joinsplit(const CPourTx &joinsplit)
|
double benchmark_verify_joinsplit(const CPourTx &joinsplit)
|
||||||
{
|
{
|
||||||
timer_start();
|
timer_start();
|
||||||
joinsplit.Verify(*pzerocashParams);
|
// TODO: #808
|
||||||
|
uint256 pubKeyHash;
|
||||||
|
joinsplit.Verify(*pzcashParams, pubKeyHash);
|
||||||
return timer_stop();
|
return timer_stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 */
|
|
|
@ -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_ */
|
|
|
@ -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 */
|
|
|
@ -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_ */
|
|
|
@ -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 */
|
|
|
@ -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_ */
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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 */
|
|
|
@ -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_ */
|
|
||||||
|
|
|
@ -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)
|
|
|
@ -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 */
|
|
|
@ -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_ */
|
|
|
@ -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 */
|
|
|
@ -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_ */
|
|
|
@ -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 */
|
|
|
@ -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_ */
|
|
|
@ -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"
|
|
|
@ -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_ */
|
|
|
@ -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 */
|
|
|
@ -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_ */
|
|
|
@ -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_ */
|
|
|
@ -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 */
|
|
|
@ -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_ */
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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_
|
|
|
@ -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
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -4,8 +4,8 @@ set -eu
|
||||||
|
|
||||||
PARAMS_DIR="$HOME/.zcash-params"
|
PARAMS_DIR="$HOME/.zcash-params"
|
||||||
|
|
||||||
REGTEST_PKEY_NAME='zc-testnet-public-alpha-proving.key'
|
REGTEST_PKEY_NAME='z3-proving.key'
|
||||||
REGTEST_VKEY_NAME='zc-testnet-public-alpha-verification.key'
|
REGTEST_VKEY_NAME='z3-verification.key'
|
||||||
REGTEST_PKEY_URL="https://z.cash/downloads/$REGTEST_PKEY_NAME"
|
REGTEST_PKEY_URL="https://z.cash/downloads/$REGTEST_PKEY_NAME"
|
||||||
REGTEST_VKEY_URL="https://z.cash/downloads/$REGTEST_VKEY_NAME"
|
REGTEST_VKEY_URL="https://z.cash/downloads/$REGTEST_VKEY_NAME"
|
||||||
REGTEST_DIR="$PARAMS_DIR/regtest"
|
REGTEST_DIR="$PARAMS_DIR/regtest"
|
||||||
|
@ -86,9 +86,9 @@ cd "$PARAMS_DIR"
|
||||||
# Now verify their hashes:
|
# Now verify their hashes:
|
||||||
echo 'Verifying parameter file integrity via sha256sum...'
|
echo 'Verifying parameter file integrity via sha256sum...'
|
||||||
sha256sum --check - <<EOF
|
sha256sum --check - <<EOF
|
||||||
7844a96933979158886a5b69fb163f49de76120fa1dcfc33b16c83c134e61817 regtest/$REGTEST_PKEY_NAME
|
1f16beeafe4f0a22cc6d0ea07bdacb083dd10bfd5ce755f72fb5eaeba0ba7286 regtest/$REGTEST_PKEY_NAME
|
||||||
7844a96933979158886a5b69fb163f49de76120fa1dcfc33b16c83c134e61817 testnet3/$REGTEST_PKEY_NAME
|
1f16beeafe4f0a22cc6d0ea07bdacb083dd10bfd5ce755f72fb5eaeba0ba7286 testnet3/$REGTEST_PKEY_NAME
|
||||||
6902fd687bface72e572a7cda57f6da5a0c606c7b9769f30becd255e57924f41 regtest/$REGTEST_VKEY_NAME
|
3840f3192c987a032fc1855e0a6081b62ae9df98172c9d68e7ecf8bb38b18426 regtest/$REGTEST_VKEY_NAME
|
||||||
6902fd687bface72e572a7cda57f6da5a0c606c7b9769f30becd255e57924f41 testnet3/$REGTEST_VKEY_NAME
|
3840f3192c987a032fc1855e0a6081b62ae9df98172c9d68e7ecf8bb38b18426 testnet3/$REGTEST_VKEY_NAME
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue