Major updates integration from all upstreams
This commit is contained in:
164
src/Makefile.am
164
src/Makefile.am
@@ -3,6 +3,7 @@ DIST_SUBDIRS = secp256k1 univalue cryptoconditions
|
||||
AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS) $(SAN_LDFLAGS) $(HARDENED_LDFLAGS)
|
||||
AM_CXXFLAGS = $(SAN_CXXFLAGS) $(HARDENED_CXXFLAGS) $(ERROR_CXXFLAGS)
|
||||
AM_CPPFLAGS = $(HARDENED_CPPFLAGS)
|
||||
EXTRA_LIBRARIES =
|
||||
|
||||
if EMBEDDED_LEVELDB
|
||||
LEVELDB_CPPFLAGS += -I$(srcdir)/leveldb/include
|
||||
@@ -20,7 +21,7 @@ $(LIBLEVELDB) $(LIBMEMENV):
|
||||
endif
|
||||
|
||||
BITCOIN_CONFIG_INCLUDES=-I$(builddir)/config
|
||||
BITCOIN_INCLUDES=-I$(builddir) -I$(builddir)/obj $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) $(CRYPTO_CFLAGS) $(SSL_CFLAGS)
|
||||
BITCOIN_INCLUDES=-I$(builddir) -I$(builddir)/obj $(BDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) $(CRYPTO_CFLAGS) $(SSL_CFLAGS)
|
||||
|
||||
BITCOIN_INCLUDES += -I$(srcdir)/secp256k1/include
|
||||
BITCOIN_INCLUDES += -I$(srcdir)/cryptoconditions/include
|
||||
@@ -34,7 +35,7 @@ endif
|
||||
if TARGET_DARWIN
|
||||
LIBBITCOIN_SERVER=libbitcoin_server.a -lcurl
|
||||
else
|
||||
LIBBITCOIN_SERVER=libbitcoin_server.a
|
||||
LIBBITCOIN_SERVER=libbitcoin_server.a -lcurl
|
||||
endif
|
||||
|
||||
LIBBITCOIN_WALLET=libbitcoin_wallet.a
|
||||
@@ -48,57 +49,68 @@ LIBSECP256K1=secp256k1/libsecp256k1.la
|
||||
LIBCRYPTOCONDITIONS=cryptoconditions/libcryptoconditions_core.la
|
||||
LIBSNARK=snark/libsnark.a
|
||||
LIBUNIVALUE=univalue/libunivalue.la
|
||||
LIBZCASH=libzcash.a -lcurl
|
||||
LIBZCASH=libzcash.a
|
||||
|
||||
if ENABLE_ZMQ
|
||||
LIBBITCOIN_ZMQ=libbitcoin_zmq.a
|
||||
endif
|
||||
if ENABLE_PROTON
|
||||
LIBBITCOIN_PROTON=libbitcoin_proton.a
|
||||
endif
|
||||
if BUILD_BITCOIN_LIBS
|
||||
LIBZCASH_CONSENSUS=libzcashconsensus.la
|
||||
endif
|
||||
if ENABLE_WALLET
|
||||
LIBBITCOIN_WALLET=libbitcoin_wallet.a
|
||||
endif
|
||||
|
||||
$(LIBSECP256K1): $(wildcard secp256k1/src/*) $(wildcard secp256k1/include/*)
|
||||
$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) OPTFLAGS="-O2 -march=x86-64 -g "
|
||||
|
||||
LIBSNARK_CXXFLAGS = -fPIC -DBINARY_OUTPUT -DNO_PT_COMPRESSION=1 -fstack-protector-all
|
||||
LIBSNARK_CXXFLAGS = $(AM_CXXFLAGS) $(PIC_FLAGS) -DBINARY_OUTPUT -DNO_PT_COMPRESSION=1 -fstack-protector-all
|
||||
LIBSNARK_CONFIG_FLAGS = CURVE=ALT_BN128 NO_PROCPS=1 NO_DOCS=1 STATIC=1 NO_SUPERCOP=1 FEATUREFLAGS=-DMONTGOMERY_OUTPUT NO_COPY_DEPINST=1 NO_COMPILE_LIBGTEST=1
|
||||
if HAVE_OPENMP
|
||||
LIBSNARK_CONFIG_FLAGS += MULTICORE=1
|
||||
endif
|
||||
if TARGET_DARWIN
|
||||
LIBSNARK_CONFIG_FLAGS += PLATFORM=darwin
|
||||
endif
|
||||
|
||||
$(LIBSNARK): $(wildcard snark/src/*)
|
||||
$(AM_V_at) CXXFLAGS="$(LIBSNARK_CXXFLAGS)" $(MAKE) $(AM_MAKEFLAGS) -C snark/ DEPINST="$(LIBSNARK_DEPINST)" $(LIBSNARK_CONFIG_FLAGS) OPTFLAGS="-O2 -march=x86-64 -g "
|
||||
$(AM_V_at) CC="$(CC)" CXX="$(CXX)" AR="$(AR)" CXXFLAGS="$(LIBSNARK_CXXFLAGS)" $(MAKE) $(AM_MAKEFLAGS) -C snark/ DEPINST="$(LIBSNARK_DEPINST)" $(LIBSNARK_CONFIG_FLAGS) OPTFLAGS="-O2 -march=x86-64"
|
||||
|
||||
libsnark-tests: $(wildcard snark/src/*)
|
||||
$(AM_V_at) CXXFLAGS="$(LIBSNARK_CXXFLAGS)" $(MAKE) $(AM_MAKEFLAGS) -C snark/ check DEPINST="$(LIBSNARK_DEPINST)" $(LIBSNARK_CONFIG_FLAGS) OPTFLAGS="-O2 -march=x86-64 -g "
|
||||
$(AM_V_at) CC="$(CC)" CXX="$(CXX)" AR="$(AR)" CXXFLAGS="$(LIBSNARK_CXXFLAGS)" $(MAKE) $(AM_MAKEFLAGS) -C snark/ check DEPINST="$(LIBSNARK_DEPINST)" $(LIBSNARK_CONFIG_FLAGS) OPTFLAGS="-O2 -march=x86-64"
|
||||
|
||||
$(LIBUNIVALUE): $(wildcard univalue/lib/*)
|
||||
$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C univalue/ OPTFLAGS="-O2 -march=x86-64 -g "
|
||||
$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) OPTFLAGS="-O2 -march=x86-64 -g "
|
||||
|
||||
$(LIBCRYPTOCONDITIONS): $(wildcard cryptoconditions/src/*) $(wildcard cryptoconditions/include/*)
|
||||
$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) OPTFLAGS="-O2 -march=x86-64 -g "
|
||||
|
||||
# Make is not made aware of per-object dependencies to avoid limiting building parallelization
|
||||
# But to build the less dependent modules first, we manually select their order here:
|
||||
EXTRA_LIBRARIES = \
|
||||
crypto/libbitcoin_crypto.a \
|
||||
crypto/libverus_crypto.a \
|
||||
crypto/libverus_portable_crypto.a \
|
||||
libbitcoin_util.a \
|
||||
libbitcoin_common.a \
|
||||
libbitcoin_server.a \
|
||||
libbitcoin_cli.a \
|
||||
EXTRA_LIBRARIES += \
|
||||
$(LIBBITCOIN_CRYPTO) \
|
||||
$(LIBVERUS_CRYPTO) \
|
||||
$(LIBVERUS_PORTABLE_CRYPTO) \
|
||||
$(LIBBITCOIN_UTIL) \
|
||||
$(LIBBITCOIN_COMMON) \
|
||||
$(LIBBITCOIN_SERVER) \
|
||||
$(LIBBITCOIN_CLI) \
|
||||
libzcash.a
|
||||
if ENABLE_WALLET
|
||||
BITCOIN_INCLUDES += $(BDB_CPPFLAGS)
|
||||
EXTRA_LIBRARIES += libbitcoin_wallet.a
|
||||
EXTRA_LIBRARIES += $(LIBBITCOIN_WALLET)
|
||||
endif
|
||||
if ENABLE_ZMQ
|
||||
EXTRA_LIBRARIES += libbitcoin_zmq.a
|
||||
EXTRA_LIBRARIES += $(LIBBITCOIN_ZMQ)
|
||||
endif
|
||||
if ENABLE_PROTON
|
||||
EXTRA_LIBRARIES += libbitcoin_proton.a
|
||||
EXTRA_LIBRARIES += $(LIBBITCOIN_PROTON)
|
||||
endif
|
||||
|
||||
if BUILD_BITCOIN_LIBS
|
||||
lib_LTLIBRARIES = libzcashconsensus.la
|
||||
LIBZCASH_CONSENSUS=libzcashconsensus.la
|
||||
else
|
||||
LIBZCASH_CONSENSUS=
|
||||
endif
|
||||
lib_LTLIBRARIES = $(LIBZCASH_CONSENSUS)
|
||||
|
||||
bin_PROGRAMS =
|
||||
noinst_PROGRAMS =
|
||||
@@ -125,7 +137,8 @@ LIBZCASH_H = \
|
||||
zcash/prf.h \
|
||||
zcash/Proof.hpp \
|
||||
zcash/util.h \
|
||||
zcash/Zcash.h
|
||||
zcash/Zcash.h \
|
||||
zcash/zip32.h
|
||||
|
||||
.PHONY: FORCE collate-libsnark check-symbols check-security
|
||||
# bitcoin core #
|
||||
@@ -144,6 +157,7 @@ BITCOIN_CORE_H = \
|
||||
asyncrpcoperation.h \
|
||||
asyncrpcqueue.h \
|
||||
base58.h \
|
||||
bech32.h \
|
||||
bloom.h \
|
||||
cc/eval.h \
|
||||
chain.h \
|
||||
@@ -175,8 +189,9 @@ BITCOIN_CORE_H = \
|
||||
httpserver.h \
|
||||
init.h \
|
||||
key.h \
|
||||
key_io.h \
|
||||
keystore.h \
|
||||
leveldbwrapper.h \
|
||||
dbwrapper.h \
|
||||
limitedmap.h \
|
||||
main.h \
|
||||
memusage.h \
|
||||
@@ -191,6 +206,7 @@ BITCOIN_CORE_H = \
|
||||
paymentdisclosuredb.h \
|
||||
policy/fees.h \
|
||||
pow.h \
|
||||
prevector.h \
|
||||
primitives/block.h \
|
||||
primitives/transaction.h \
|
||||
primitives/nonce.h \
|
||||
@@ -198,9 +214,10 @@ BITCOIN_CORE_H = \
|
||||
pubkey.h \
|
||||
random.h \
|
||||
reverselock.h \
|
||||
rpcclient.h \
|
||||
rpcprotocol.h \
|
||||
rpcserver.h \
|
||||
rpc/client.h \
|
||||
rpc/protocol.h \
|
||||
rpc/server.h \
|
||||
rpc/register.h \
|
||||
scheduler.h \
|
||||
script/interpreter.h \
|
||||
script/script.h \
|
||||
@@ -220,6 +237,7 @@ BITCOIN_CORE_H = \
|
||||
timedata.h \
|
||||
tinyformat.h \
|
||||
torcontrol.h \
|
||||
transaction_builder.h \
|
||||
txdb.h \
|
||||
txmempool.h \
|
||||
ui_interface.h \
|
||||
@@ -237,6 +255,7 @@ BITCOIN_CORE_H = \
|
||||
wallet/asyncrpcoperation_shieldcoinbase.h \
|
||||
wallet/crypter.h \
|
||||
wallet/db.h \
|
||||
wallet/rpcwallet.h \
|
||||
wallet/wallet.h \
|
||||
wallet/wallet_ismine.h \
|
||||
wallet/walletdb.h \
|
||||
@@ -297,10 +316,10 @@ libbitcoin_server_a_SOURCES = \
|
||||
httprpc.cpp \
|
||||
httpserver.cpp \
|
||||
init.cpp \
|
||||
leveldbwrapper.cpp \
|
||||
dbwrapper.cpp \
|
||||
main.cpp \
|
||||
merkleblock.cpp \
|
||||
metrics.cpp \
|
||||
metrics.h \
|
||||
miner.cpp \
|
||||
net.cpp \
|
||||
noui.cpp \
|
||||
@@ -310,14 +329,15 @@ libbitcoin_server_a_SOURCES = \
|
||||
policy/fees.cpp \
|
||||
pow.cpp \
|
||||
rest.cpp \
|
||||
rpcblockchain.cpp \
|
||||
rpccrosschain.cpp \
|
||||
rpcmining.cpp \
|
||||
rpcmisc.cpp \
|
||||
rpcnet.cpp \
|
||||
rpcrawtransaction.cpp \
|
||||
rpcserver.cpp \
|
||||
rpc/blockchain.cpp \
|
||||
rpc/crosschain.cpp \
|
||||
rpc/mining.cpp \
|
||||
rpc/misc.cpp \
|
||||
rpc/net.cpp \
|
||||
rpc/rawtransaction.cpp \
|
||||
rpc/server.cpp \
|
||||
script/serverchecker.cpp \
|
||||
script/sigcache.cpp \
|
||||
timedata.cpp \
|
||||
torcontrol.cpp \
|
||||
txdb.cpp \
|
||||
@@ -327,8 +347,6 @@ libbitcoin_server_a_SOURCES = \
|
||||
$(LIBZCASH_H)
|
||||
|
||||
if ENABLE_ZMQ
|
||||
LIBBITCOIN_ZMQ=libbitcoin_zmq.a
|
||||
|
||||
libbitcoin_zmq_a_CPPFLAGS = $(BITCOIN_INCLUDES) $(ZMQ_CFLAGS)
|
||||
libbitcoin_zmq_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
||||
libbitcoin_zmq_a_SOURCES = \
|
||||
@@ -338,8 +356,6 @@ libbitcoin_zmq_a_SOURCES = \
|
||||
endif
|
||||
|
||||
if ENABLE_PROTON
|
||||
LIBBITCOIN_PROTON=libbitcoin_proton.a
|
||||
|
||||
libbitcoin_proton_a_CPPFLAGS = $(BITCOIN_INCLUDES)
|
||||
libbitcoin_proton_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
||||
libbitcoin_proton_a_SOURCES = \
|
||||
@@ -363,6 +379,7 @@ libbitcoin_wallet_a_SOURCES = \
|
||||
wallet/db.cpp \
|
||||
paymentdisclosure.cpp \
|
||||
paymentdisclosuredb.cpp \
|
||||
transaction_builder.cpp \
|
||||
wallet/rpcdisclosure.cpp \
|
||||
wallet/rpcdump.cpp \
|
||||
cc/CCassetstx.cpp \
|
||||
@@ -371,6 +388,7 @@ libbitcoin_wallet_a_SOURCES = \
|
||||
wallet/wallet.cpp \
|
||||
wallet/wallet_ismine.cpp \
|
||||
wallet/walletdb.cpp \
|
||||
zcash/zip32.cpp \
|
||||
$(BITCOIN_CORE_H) \
|
||||
$(LIBZCASH_H)
|
||||
|
||||
@@ -432,6 +450,7 @@ libbitcoin_common_a_SOURCES = \
|
||||
amount.cpp \
|
||||
arith_uint256.cpp \
|
||||
base58.cpp \
|
||||
bech32.cpp \
|
||||
chainparams.cpp \
|
||||
coins.cpp \
|
||||
compressor.cpp \
|
||||
@@ -445,8 +464,10 @@ libbitcoin_common_a_SOURCES = \
|
||||
hash.cpp \
|
||||
importcoin.cpp \
|
||||
key.cpp \
|
||||
key_io.cpp \
|
||||
keystore.cpp \
|
||||
netbase.cpp \
|
||||
metrics.cpp \
|
||||
primitives/block.cpp \
|
||||
primitives/transaction.cpp \
|
||||
primitives/nonce.cpp \
|
||||
@@ -461,6 +482,7 @@ libbitcoin_common_a_SOURCES = \
|
||||
script/sign.cpp \
|
||||
script/standard.cpp \
|
||||
veruslaunch.cpp \
|
||||
transaction_builder.cpp \
|
||||
$(BITCOIN_CORE_H) \
|
||||
$(LIBZCASH_H)
|
||||
|
||||
@@ -470,23 +492,23 @@ libbitcoin_common_a_SOURCES = \
|
||||
libbitcoin_util_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
|
||||
libbitcoin_util_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
||||
libbitcoin_util_a_SOURCES = \
|
||||
support/pagelocker.cpp \
|
||||
chainparamsbase.cpp \
|
||||
clientversion.cpp \
|
||||
compat/glibc_sanity.cpp \
|
||||
compat/glibcxx_sanity.cpp \
|
||||
compat/strnlen.cpp \
|
||||
random.cpp \
|
||||
rpcprotocol.cpp \
|
||||
support/cleanse.cpp \
|
||||
sync.cpp \
|
||||
uint256.cpp \
|
||||
util.cpp \
|
||||
utilmoneystr.cpp \
|
||||
utilstrencodings.cpp \
|
||||
utiltime.cpp \
|
||||
$(BITCOIN_CORE_H) \
|
||||
$(LIBZCASH_H)
|
||||
support/pagelocker.cpp \
|
||||
chainparamsbase.cpp \
|
||||
clientversion.cpp \
|
||||
compat/glibc_sanity.cpp \
|
||||
compat/glibcxx_sanity.cpp \
|
||||
compat/strnlen.cpp \
|
||||
random.cpp \
|
||||
rpc/protocol.cpp \
|
||||
support/cleanse.cpp \
|
||||
sync.cpp \
|
||||
uint256.cpp \
|
||||
util.cpp \
|
||||
utilmoneystr.cpp \
|
||||
utilstrencodings.cpp \
|
||||
utiltime.cpp \
|
||||
$(BITCOIN_CORE_H) \
|
||||
$(LIBZCASH_H)
|
||||
|
||||
if GLIBC_BACK_COMPAT
|
||||
libbitcoin_util_a_SOURCES += compat/glibc_compat.cpp
|
||||
@@ -496,9 +518,9 @@ endif
|
||||
libbitcoin_cli_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
|
||||
libbitcoin_cli_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
||||
libbitcoin_cli_a_SOURCES = \
|
||||
rpcclient.cpp \
|
||||
$(BITCOIN_CORE_H) \
|
||||
$(LIBZCASH_H)
|
||||
rpc/client.cpp \
|
||||
$(BITCOIN_CORE_H) \
|
||||
$(LIBZCASH_H)
|
||||
|
||||
nodist_libbitcoin_util_a_SOURCES = $(srcdir)/obj/build.h
|
||||
#
|
||||
@@ -518,6 +540,8 @@ komodod_LDADD = \
|
||||
$(LIBBITCOIN_COMMON) \
|
||||
$(LIBUNIVALUE) \
|
||||
$(LIBBITCOIN_UTIL) \
|
||||
$(LIBBITCOIN_ZMQ) \
|
||||
$(LIBBITCOIN_PROTON) \
|
||||
$(LIBBITCOIN_CRYPTO) \
|
||||
$(LIBVERUS_CRYPTO) \
|
||||
$(LIBVERUS_PORTABLE_CRYPTO) \
|
||||
@@ -528,12 +552,8 @@ komodod_LDADD = \
|
||||
$(LIBSECP256K1) \
|
||||
$(LIBCRYPTOCONDITIONS)
|
||||
|
||||
if ENABLE_ZMQ
|
||||
komodod_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
|
||||
endif
|
||||
|
||||
if ENABLE_WALLET
|
||||
komodod_LDADD += libbitcoin_wallet.a
|
||||
komodod_LDADD += $(LIBBITCOIN_WALLET)
|
||||
endif
|
||||
|
||||
komodod_LDADD += \
|
||||
@@ -543,6 +563,8 @@ komodod_LDADD += \
|
||||
$(CRYPTO_LIBS) \
|
||||
$(EVENT_PTHREADS_LIBS) \
|
||||
$(EVENT_LIBS) \
|
||||
$(ZMQ_LIBS) \
|
||||
$(PROTON_LIBS) \
|
||||
$(LIBBITCOIN_CRYPTO) \
|
||||
$(LIBVERUS_CRYPTO) \
|
||||
$(LIBVERUS_PORTABLE_CRYPTO) \
|
||||
@@ -639,6 +661,7 @@ libzcash_a_SOURCES = \
|
||||
zcash/Note.cpp \
|
||||
zcash/prf.cpp \
|
||||
zcash/util.cpp \
|
||||
zcash/zip32.cpp \
|
||||
zcash/circuit/commitment.tcc \
|
||||
zcash/circuit/gadget.tcc \
|
||||
zcash/circuit/merkle.tcc \
|
||||
@@ -648,6 +671,10 @@ libzcash_a_SOURCES = \
|
||||
|
||||
libzcash_a_CPPFLAGS = -DMULTICORE -fopenmp -fPIC -DBINARY_OUTPUT -DCURVE_ALT_BN128 -DBOOST_SPIRIT_THREADSAFE -DHAVE_BUILD_INFO -D__STDC_FORMAT_MACROS $(HARDENED_CPPFLAGS) $(HARDENED_CXXFLAGS) $(HARDENED_LDFLAGS) -pipe $(SAN_LDFLAGS) -O1 -g -Wstack-protector $(SAN_CXXFLAGS) -fstack-protector-all -fPIE -fvisibility=hidden -DSTATIC $(BITCOIN_INCLUDES)
|
||||
|
||||
#libzcash_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
||||
#libzcash_a_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
|
||||
#libzcash_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMONTGOMERY_OUTPUT
|
||||
|
||||
libzcash_a_CXXFLAGS = $(SAN_CXXFLAGS) $(HARDENED_CXXFLAGS) -fwrapv -fno-strict-aliasing
|
||||
libzcash_a_LDFLAGS = $(SAN_LDFLAGS) $(HARDENED_LDFLAGS)
|
||||
libzcash_a_CPPFLAGS += -DMONTGOMERY_OUTPUT
|
||||
@@ -694,6 +721,7 @@ clean-local:
|
||||
-$(MAKE) -C leveldb clean
|
||||
-$(MAKE) -C secp256k1 clean
|
||||
-$(MAKE) -C snark clean
|
||||
-$(MAKE) -C univalue clean
|
||||
rm -f leveldb/*/*.gcno leveldb/helpers/memenv/*.gcno
|
||||
-rm -f config.h
|
||||
|
||||
@@ -726,5 +754,3 @@ include Makefile.ktest.include
|
||||
#include Makefile.test.include
|
||||
#include Makefile.gtest.include
|
||||
endif
|
||||
|
||||
include Makefile.zcash.include
|
||||
|
||||
@@ -23,6 +23,7 @@ zcash_gtest_SOURCES += \
|
||||
gtest/test_equihash.cpp \
|
||||
gtest/test_httprpc.cpp \
|
||||
gtest/test_joinsplit.cpp \
|
||||
gtest/test_keys.cpp \
|
||||
gtest/test_keystore.cpp \
|
||||
gtest/test_noteencryption.cpp \
|
||||
gtest/test_mempool.cpp \
|
||||
@@ -32,7 +33,9 @@ zcash_gtest_SOURCES += \
|
||||
gtest/test_pow.cpp \
|
||||
gtest/test_random.cpp \
|
||||
gtest/test_rpc.cpp \
|
||||
gtest/test_sapling_note.cpp \
|
||||
gtest/test_transaction.cpp \
|
||||
gtest/test_transaction_builder.cpp \
|
||||
gtest/test_upgrades.cpp \
|
||||
gtest/test_validation.cpp \
|
||||
gtest/test_circuit.cpp \
|
||||
@@ -40,7 +43,9 @@ zcash_gtest_SOURCES += \
|
||||
gtest/test_libzcash_utils.cpp \
|
||||
gtest/test_proofs.cpp \
|
||||
gtest/test_paymentdisclosure.cpp \
|
||||
gtest/test_checkblock.cpp
|
||||
gtest/test_pedersen_hash.cpp \
|
||||
gtest/test_checkblock.cpp \
|
||||
gtest/test_zip32.cpp
|
||||
if ENABLE_WALLET
|
||||
zcash_gtest_SOURCES += \
|
||||
wallet/gtest/test_wallet.cpp
|
||||
|
||||
@@ -20,78 +20,89 @@ EXTRA_DIST += \
|
||||
test/data/wallet.dat
|
||||
|
||||
JSON_TEST_FILES = \
|
||||
test/data/script_valid.json \
|
||||
test/data/base58_keys_valid.json \
|
||||
test/data/base58_encode_decode.json \
|
||||
test/data/base58_keys_invalid.json \
|
||||
test/data/script_invalid.json \
|
||||
test/data/tx_invalid.json \
|
||||
test/data/tx_valid.json \
|
||||
test/data/sighash.json \
|
||||
test/data/merkle_roots.json \
|
||||
test/data/merkle_roots_empty.json \
|
||||
test/data/merkle_serialization.json \
|
||||
test/data/merkle_witness_serialization.json \
|
||||
test/data/merkle_path.json \
|
||||
test/data/merkle_commitments.json \
|
||||
test/data/g1_compressed.json \
|
||||
test/data/g2_compressed.json
|
||||
test/data/script_valid.json \
|
||||
test/data/base58_keys_valid.json \
|
||||
test/data/base58_encode_decode.json \
|
||||
test/data/base58_keys_invalid.json \
|
||||
test/data/script_invalid.json \
|
||||
test/data/tx_invalid.json \
|
||||
test/data/tx_valid.json \
|
||||
test/data/sighash.json \
|
||||
test/data/merkle_roots.json \
|
||||
test/data/merkle_roots_empty.json \
|
||||
test/data/merkle_serialization.json \
|
||||
test/data/merkle_witness_serialization.json \
|
||||
test/data/merkle_path.json \
|
||||
test/data/merkle_commitments.json \
|
||||
test/data/merkle_roots_sapling.json \
|
||||
test/data/merkle_roots_empty_sapling.json \
|
||||
test/data/merkle_serialization_sapling.json \
|
||||
test/data/merkle_witness_serialization_sapling.json \
|
||||
test/data/merkle_path_sapling.json \
|
||||
test/data/merkle_commitments_sapling.json \
|
||||
test/data/g1_compressed.json \
|
||||
test/data/g2_compressed.json \
|
||||
test/data/sapling_key_components.json
|
||||
|
||||
RAW_TEST_FILES = test/data/alertTests.raw
|
||||
|
||||
GENERATED_TEST_FILES = $(JSON_TEST_FILES:.json=.json.h) $(RAW_TEST_FILES:.raw=.raw.h)
|
||||
|
||||
BITCOIN_TESTS =\
|
||||
test/arith_uint256_tests.cpp \
|
||||
test/bignum.h \
|
||||
test/addrman_tests.cpp \
|
||||
test/allocator_tests.cpp \
|
||||
test/base32_tests.cpp \
|
||||
test/base58_tests.cpp \
|
||||
test/base64_tests.cpp \
|
||||
test/bip32_tests.cpp \
|
||||
test/bloom_tests.cpp \
|
||||
test/checkblock_tests.cpp \
|
||||
test/Checkpoints_tests.cpp \
|
||||
test/coins_tests.cpp \
|
||||
test/compress_tests.cpp \
|
||||
test/crypto_tests.cpp \
|
||||
test/DoS_tests.cpp \
|
||||
test/equihash_tests.cpp \
|
||||
test/getarg_tests.cpp \
|
||||
test/hash_tests.cpp \
|
||||
test/key_tests.cpp \
|
||||
test/main_tests.cpp \
|
||||
test/mempool_tests.cpp \
|
||||
test/miner_tests.cpp \
|
||||
test/mruset_tests.cpp \
|
||||
test/multisig_tests.cpp \
|
||||
test/netbase_tests.cpp \
|
||||
test/pmt_tests.cpp \
|
||||
test/policyestimator_tests.cpp \
|
||||
test/pow_tests.cpp \
|
||||
test/raii_event_tests.cpp \
|
||||
test/reverselock_tests.cpp \
|
||||
test/rpc_tests.cpp \
|
||||
test/sanity_tests.cpp \
|
||||
test/scheduler_tests.cpp \
|
||||
test/script_P2SH_tests.cpp \
|
||||
test/script_P2PKH_tests.cpp \
|
||||
test/script_tests.cpp \
|
||||
test/scriptnum_tests.cpp \
|
||||
test/serialize_tests.cpp \
|
||||
test/sighash_tests.cpp \
|
||||
test/sigopcount_tests.cpp \
|
||||
test/skiplist_tests.cpp \
|
||||
test/test_bitcoin.cpp \
|
||||
test/test_bitcoin.h \
|
||||
test/timedata_tests.cpp \
|
||||
test/torcontrol_tests.cpp \
|
||||
test/transaction_tests.cpp \
|
||||
test/uint256_tests.cpp \
|
||||
test/univalue_tests.cpp \
|
||||
test/util_tests.cpp \
|
||||
test/sha256compress_tests.cpp
|
||||
test/arith_uint256_tests.cpp \
|
||||
test/bignum.h \
|
||||
test/addrman_tests.cpp \
|
||||
test/alert_tests.cpp \
|
||||
test/allocator_tests.cpp \
|
||||
test/base32_tests.cpp \
|
||||
test/base58_tests.cpp \
|
||||
test/base64_tests.cpp \
|
||||
test/bech32_tests.cpp \
|
||||
test/bip32_tests.cpp \
|
||||
test/bloom_tests.cpp \
|
||||
test/checkblock_tests.cpp \
|
||||
test/Checkpoints_tests.cpp \
|
||||
test/coins_tests.cpp \
|
||||
test/compress_tests.cpp \
|
||||
test/convertbits_tests.cpp \
|
||||
test/crypto_tests.cpp \
|
||||
test/DoS_tests.cpp \
|
||||
test/equihash_tests.cpp \
|
||||
test/getarg_tests.cpp \
|
||||
test/hash_tests.cpp \
|
||||
test/key_tests.cpp \
|
||||
test/dbwrapper_tests.cpp \
|
||||
test/main_tests.cpp \
|
||||
test/mempool_tests.cpp \
|
||||
test/miner_tests.cpp \
|
||||
test/mruset_tests.cpp \
|
||||
test/multisig_tests.cpp \
|
||||
test/netbase_tests.cpp \
|
||||
test/pmt_tests.cpp \
|
||||
test/policyestimator_tests.cpp \
|
||||
test/pow_tests.cpp \
|
||||
test/prevector_tests.cpp \
|
||||
test/raii_event_tests.cpp \
|
||||
test/reverselock_tests.cpp \
|
||||
test/rpc_tests.cpp \
|
||||
test/sanity_tests.cpp \
|
||||
test/scheduler_tests.cpp \
|
||||
test/script_P2SH_tests.cpp \
|
||||
test/script_tests.cpp \
|
||||
test/scriptnum_tests.cpp \
|
||||
test/serialize_tests.cpp \
|
||||
test/sighash_tests.cpp \
|
||||
test/sigopcount_tests.cpp \
|
||||
test/skiplist_tests.cpp \
|
||||
test/test_bitcoin.cpp \
|
||||
test/test_bitcoin.h \
|
||||
test/timedata_tests.cpp \
|
||||
test/torcontrol_tests.cpp \
|
||||
test/transaction_tests.cpp \
|
||||
test/uint256_tests.cpp \
|
||||
test/univalue_tests.cpp \
|
||||
test/util_tests.cpp \
|
||||
test/sha256compress_tests.cpp
|
||||
|
||||
if ENABLE_WALLET
|
||||
BITCOIN_TESTS += \
|
||||
@@ -108,6 +119,9 @@ test_test_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
||||
if ENABLE_WALLET
|
||||
test_test_bitcoin_LDADD += $(LIBBITCOIN_WALLET)
|
||||
endif
|
||||
test_test_bitcoin_LDADD += $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) \
|
||||
$(LIBLEVELDB) $(LIBMEMENV) $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_LIBS) $(EVENT_PTHREADS_LIBS)
|
||||
test_test_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
||||
|
||||
test_test_bitcoin_LDADD += $(LIBZCASH_CONSENSUS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(LIBZCASH) $(LIBSNARK) $(LIBZCASH_LIBS)
|
||||
test_test_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -static
|
||||
|
||||
@@ -54,7 +54,7 @@ public:
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(*(CAddress*)this);
|
||||
READWRITE(source);
|
||||
READWRITE(nLastSuccess);
|
||||
@@ -279,7 +279,7 @@ public:
|
||||
* very little in common.
|
||||
*/
|
||||
template<typename Stream>
|
||||
void Serialize(Stream &s, int nType, int nVersionDummy) const
|
||||
void Serialize(Stream &s) const
|
||||
{
|
||||
LOCK(cs);
|
||||
|
||||
@@ -329,7 +329,7 @@ public:
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
void Unserialize(Stream& s, int nType, int nVersionDummy)
|
||||
void Unserialize(Stream& s)
|
||||
{
|
||||
LOCK(cs);
|
||||
|
||||
@@ -434,11 +434,6 @@ public:
|
||||
Check();
|
||||
}
|
||||
|
||||
unsigned int GetSerializeSize(int nType, int nVersion) const
|
||||
{
|
||||
return (CSizeComputer(nType, nVersion) << *this).size();
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
std::vector<int>().swap(vRandom);
|
||||
|
||||
@@ -49,9 +49,8 @@ public:
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(this->nVersion);
|
||||
nVersion = this->nVersion;
|
||||
READWRITE(nRelayUntil);
|
||||
READWRITE(nExpiration);
|
||||
READWRITE(nID);
|
||||
@@ -87,7 +86,7 @@ public:
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(vchMsg);
|
||||
READWRITE(vchSig);
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ public:
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(nSatoshisPerK);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -16,7 +16,7 @@ using namespace std;
|
||||
|
||||
static boost::uuids::random_generator uuidgen;
|
||||
|
||||
std::map<OperationStatus, std::string> OperationStatusMap = {
|
||||
static std::map<OperationStatus, std::string> OperationStatusMap = {
|
||||
{OperationStatus::READY, "queued"},
|
||||
{OperationStatus::EXECUTING, "executing"},
|
||||
{OperationStatus::CANCELLED, "cancelled"},
|
||||
|
||||
@@ -4,15 +4,12 @@
|
||||
|
||||
#include "base58.h"
|
||||
|
||||
#include "hash.h"
|
||||
#include "uint256.h"
|
||||
|
||||
#include "version.h"
|
||||
#include "streams.h"
|
||||
#include <hash.h>
|
||||
#include <uint256.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <boost/variant/apply_visitor.hpp>
|
||||
@@ -104,7 +101,7 @@ std::string EncodeBase58(const unsigned char* pbegin, const unsigned char* pend)
|
||||
|
||||
std::string EncodeBase58(const std::vector<unsigned char>& vch)
|
||||
{
|
||||
return EncodeBase58(&vch[0], &vch[0] + vch.size());
|
||||
return EncodeBase58(vch.data(), vch.data() + vch.size());
|
||||
}
|
||||
|
||||
bool DecodeBase58(const std::string& str, std::vector<unsigned char>& vchRet)
|
||||
@@ -143,6 +140,7 @@ bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>& vchRe
|
||||
return DecodeBase58Check(str.c_str(), vchRet);
|
||||
}
|
||||
|
||||
|
||||
CBase58Data::CBase58Data()
|
||||
{
|
||||
vchVersion.clear();
|
||||
@@ -381,27 +379,3 @@ DATA_TYPE CZCEncoding<DATA_TYPE, PREFIX, SER_SIZE>::Get() const
|
||||
ss >> ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Explicit instantiations for libzcash::PaymentAddress
|
||||
template bool CZCEncoding<libzcash::PaymentAddress,
|
||||
CChainParams::ZCPAYMENT_ADDRRESS,
|
||||
libzcash::SerializedPaymentAddressSize>::Set(const libzcash::PaymentAddress& addr);
|
||||
template libzcash::PaymentAddress CZCEncoding<libzcash::PaymentAddress,
|
||||
CChainParams::ZCPAYMENT_ADDRRESS,
|
||||
libzcash::SerializedPaymentAddressSize>::Get() const;
|
||||
|
||||
// Explicit instantiations for libzcash::ViewingKey
|
||||
template bool CZCEncoding<libzcash::ViewingKey,
|
||||
CChainParams::ZCVIEWING_KEY,
|
||||
libzcash::SerializedViewingKeySize>::Set(const libzcash::ViewingKey& vk);
|
||||
template libzcash::ViewingKey CZCEncoding<libzcash::ViewingKey,
|
||||
CChainParams::ZCVIEWING_KEY,
|
||||
libzcash::SerializedViewingKeySize>::Get() const;
|
||||
|
||||
// Explicit instantiations for libzcash::SpendingKey
|
||||
template bool CZCEncoding<libzcash::SpendingKey,
|
||||
CChainParams::ZCSPENDING_KEY,
|
||||
libzcash::SerializedSpendingKeySize>::Set(const libzcash::SpendingKey& sk);
|
||||
template libzcash::SpendingKey CZCEncoding<libzcash::SpendingKey,
|
||||
CChainParams::ZCSPENDING_KEY,
|
||||
libzcash::SerializedSpendingKeySize>::Get() const;
|
||||
|
||||
37
src/base58.h
37
src/base58.h
@@ -58,13 +58,13 @@ std::string EncodeBase58Check(const std::vector<unsigned char>& vchIn);
|
||||
* Decode a base58-encoded string (psz) that includes a checksum into a byte
|
||||
* vector (vchRet), return true if decoding is successful
|
||||
*/
|
||||
inline bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet);
|
||||
bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet);
|
||||
|
||||
/**
|
||||
* Decode a base58-encoded string (str) that includes a checksum into a byte
|
||||
* vector (vchRet), return true if decoding is successful
|
||||
*/
|
||||
inline bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>& vchRet);
|
||||
bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>& vchRet);
|
||||
|
||||
/**
|
||||
* Base class for all base58-encoded data
|
||||
@@ -107,39 +107,6 @@ public:
|
||||
DATA_TYPE Get() const;
|
||||
};
|
||||
|
||||
class CZCPaymentAddress : public CZCEncoding<libzcash::PaymentAddress, CChainParams::ZCPAYMENT_ADDRRESS, libzcash::SerializedPaymentAddressSize> {
|
||||
protected:
|
||||
std::string PrependName(const std::string& s) const { return "payment address" + s; }
|
||||
|
||||
public:
|
||||
CZCPaymentAddress() {}
|
||||
|
||||
CZCPaymentAddress(const std::string& strAddress) { SetString(strAddress.c_str(), 2); }
|
||||
CZCPaymentAddress(const libzcash::PaymentAddress& addr) { Set(addr); }
|
||||
};
|
||||
|
||||
class CZCViewingKey : public CZCEncoding<libzcash::ViewingKey, CChainParams::ZCVIEWING_KEY, libzcash::SerializedViewingKeySize> {
|
||||
protected:
|
||||
std::string PrependName(const std::string& s) const { return "viewing key" + s; }
|
||||
|
||||
public:
|
||||
CZCViewingKey() {}
|
||||
|
||||
CZCViewingKey(const std::string& strViewingKey) { SetString(strViewingKey.c_str(), 3); }
|
||||
CZCViewingKey(const libzcash::ViewingKey& vk) { Set(vk); }
|
||||
};
|
||||
|
||||
class CZCSpendingKey : public CZCEncoding<libzcash::SpendingKey, CChainParams::ZCSPENDING_KEY, libzcash::SerializedSpendingKeySize> {
|
||||
protected:
|
||||
std::string PrependName(const std::string& s) const { return "spending key" + s; }
|
||||
|
||||
public:
|
||||
CZCSpendingKey() {}
|
||||
|
||||
CZCSpendingKey(const std::string& strAddress) { SetString(strAddress.c_str(), 2); }
|
||||
CZCSpendingKey(const libzcash::SpendingKey& addr) { Set(addr); }
|
||||
};
|
||||
|
||||
/** base58-encoded Bitcoin addresses.
|
||||
* Public-key-hash-addresses have version 0 (or 111 testnet).
|
||||
* The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key.
|
||||
|
||||
194
src/bech32.cpp
Normal file
194
src/bech32.cpp
Normal file
@@ -0,0 +1,194 @@
|
||||
// Copyright (c) 2017 Pieter Wuille
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "bech32.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
typedef std::vector<uint8_t> data;
|
||||
|
||||
/** The Bech32 character set for encoding. */
|
||||
const char* CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
|
||||
|
||||
/** The Bech32 character set for decoding. */
|
||||
const int8_t CHARSET_REV[128] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1,
|
||||
-1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
|
||||
1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
|
||||
-1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
|
||||
1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1
|
||||
};
|
||||
|
||||
/** Concatenate two byte arrays. */
|
||||
data Cat(data x, const data& y)
|
||||
{
|
||||
x.insert(x.end(), y.begin(), y.end());
|
||||
return x;
|
||||
}
|
||||
|
||||
/** This function will compute what 6 5-bit values to XOR into the last 6 input values, in order to
|
||||
* make the checksum 0. These 6 values are packed together in a single 30-bit integer. The higher
|
||||
* bits correspond to earlier values. */
|
||||
uint32_t PolyMod(const data& v)
|
||||
{
|
||||
// The input is interpreted as a list of coefficients of a polynomial over F = GF(32), with an
|
||||
// implicit 1 in front. If the input is [v0,v1,v2,v3,v4], that polynomial is v(x) =
|
||||
// 1*x^5 + v0*x^4 + v1*x^3 + v2*x^2 + v3*x + v4. The implicit 1 guarantees that
|
||||
// [v0,v1,v2,...] has a distinct checksum from [0,v0,v1,v2,...].
|
||||
|
||||
// The output is a 30-bit integer whose 5-bit groups are the coefficients of the remainder of
|
||||
// v(x) mod g(x), where g(x) is the Bech32 generator,
|
||||
// x^6 + {29}x^5 + {22}x^4 + {20}x^3 + {21}x^2 + {29}x + {18}. g(x) is chosen in such a way
|
||||
// that the resulting code is a BCH code, guaranteeing detection of up to 3 errors within a
|
||||
// window of 1023 characters. Among the various possible BCH codes, one was selected to in
|
||||
// fact guarantee detection of up to 4 errors within a window of 89 characters.
|
||||
|
||||
// Note that the coefficients are elements of GF(32), here represented as decimal numbers
|
||||
// between {}. In this finite field, addition is just XOR of the corresponding numbers. For
|
||||
// example, {27} + {13} = {27 ^ 13} = {22}. Multiplication is more complicated, and requires
|
||||
// treating the bits of values themselves as coefficients of a polynomial over a smaller field,
|
||||
// GF(2), and multiplying those polynomials mod a^5 + a^3 + 1. For example, {5} * {26} =
|
||||
// (a^2 + 1) * (a^4 + a^3 + a) = (a^4 + a^3 + a) * a^2 + (a^4 + a^3 + a) = a^6 + a^5 + a^4 + a
|
||||
// = a^3 + 1 (mod a^5 + a^3 + 1) = {9}.
|
||||
|
||||
// During the course of the loop below, `c` contains the bitpacked coefficients of the
|
||||
// polynomial constructed from just the values of v that were processed so far, mod g(x). In
|
||||
// the above example, `c` initially corresponds to 1 mod (x), and after processing 2 inputs of
|
||||
// v, it corresponds to x^2 + v0*x + v1 mod g(x). As 1 mod g(x) = 1, that is the starting value
|
||||
// for `c`.
|
||||
uint32_t c = 1;
|
||||
for (auto v_i : v) {
|
||||
// We want to update `c` to correspond to a polynomial with one extra term. If the initial
|
||||
// value of `c` consists of the coefficients of c(x) = f(x) mod g(x), we modify it to
|
||||
// correspond to c'(x) = (f(x) * x + v_i) mod g(x), where v_i is the next input to
|
||||
// process. Simplifying:
|
||||
// c'(x) = (f(x) * x + v_i) mod g(x)
|
||||
// ((f(x) mod g(x)) * x + v_i) mod g(x)
|
||||
// (c(x) * x + v_i) mod g(x)
|
||||
// If c(x) = c0*x^5 + c1*x^4 + c2*x^3 + c3*x^2 + c4*x + c5, we want to compute
|
||||
// c'(x) = (c0*x^5 + c1*x^4 + c2*x^3 + c3*x^2 + c4*x + c5) * x + v_i mod g(x)
|
||||
// = c0*x^6 + c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i mod g(x)
|
||||
// = c0*(x^6 mod g(x)) + c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i
|
||||
// If we call (x^6 mod g(x)) = k(x), this can be written as
|
||||
// c'(x) = (c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i) + c0*k(x)
|
||||
|
||||
// First, determine the value of c0:
|
||||
uint8_t c0 = c >> 25;
|
||||
|
||||
// Then compute c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i:
|
||||
c = ((c & 0x1ffffff) << 5) ^ v_i;
|
||||
|
||||
// Finally, for each set bit n in c0, conditionally add {2^n}k(x):
|
||||
if (c0 & 1) c ^= 0x3b6a57b2; // k(x) = {29}x^5 + {22}x^4 + {20}x^3 + {21}x^2 + {29}x + {18}
|
||||
if (c0 & 2) c ^= 0x26508e6d; // {2}k(x) = {19}x^5 + {5}x^4 + x^3 + {3}x^2 + {19}x + {13}
|
||||
if (c0 & 4) c ^= 0x1ea119fa; // {4}k(x) = {15}x^5 + {10}x^4 + {2}x^3 + {6}x^2 + {15}x + {26}
|
||||
if (c0 & 8) c ^= 0x3d4233dd; // {8}k(x) = {30}x^5 + {20}x^4 + {4}x^3 + {12}x^2 + {30}x + {29}
|
||||
if (c0 & 16) c ^= 0x2a1462b3; // {16}k(x) = {21}x^5 + x^4 + {8}x^3 + {24}x^2 + {21}x + {19}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/** Convert to lower case. */
|
||||
inline unsigned char LowerCase(unsigned char c)
|
||||
{
|
||||
return (c >= 'A' && c <= 'Z') ? (c - 'A') + 'a' : c;
|
||||
}
|
||||
|
||||
/** Expand a HRP for use in checksum computation. */
|
||||
data ExpandHRP(const std::string& hrp)
|
||||
{
|
||||
data ret;
|
||||
ret.reserve(hrp.size() + 90);
|
||||
ret.resize(hrp.size() * 2 + 1);
|
||||
for (size_t i = 0; i < hrp.size(); ++i) {
|
||||
unsigned char c = hrp[i];
|
||||
ret[i] = c >> 5;
|
||||
ret[i + hrp.size() + 1] = c & 0x1f;
|
||||
}
|
||||
ret[hrp.size()] = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Verify a checksum. */
|
||||
bool VerifyChecksum(const std::string& hrp, const data& values)
|
||||
{
|
||||
// PolyMod computes what value to xor into the final values to make the checksum 0. However,
|
||||
// if we required that the checksum was 0, it would be the case that appending a 0 to a valid
|
||||
// list of values would result in a new valid list. For that reason, Bech32 requires the
|
||||
// resulting checksum to be 1 instead.
|
||||
return PolyMod(Cat(ExpandHRP(hrp), values)) == 1;
|
||||
}
|
||||
|
||||
/** Create a checksum. */
|
||||
data CreateChecksum(const std::string& hrp, const data& values)
|
||||
{
|
||||
data enc = Cat(ExpandHRP(hrp), values);
|
||||
enc.resize(enc.size() + 6); // Append 6 zeroes
|
||||
uint32_t mod = PolyMod(enc) ^ 1; // Determine what to XOR into those 6 zeroes.
|
||||
data ret(6);
|
||||
for (size_t i = 0; i < 6; ++i) {
|
||||
// Convert the 5-bit groups in mod to checksum values.
|
||||
ret[i] = (mod >> (5 * (5 - i))) & 31;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace bech32
|
||||
{
|
||||
|
||||
/** Encode a Bech32 string. */
|
||||
std::string Encode(const std::string& hrp, const data& values) {
|
||||
data checksum = CreateChecksum(hrp, values);
|
||||
data combined = Cat(values, checksum);
|
||||
std::string ret = hrp + '1';
|
||||
ret.reserve(ret.size() + combined.size());
|
||||
for (auto c : combined) {
|
||||
if (c >= 32) {
|
||||
return "";
|
||||
}
|
||||
ret += CHARSET[c];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Decode a Bech32 string. */
|
||||
std::pair<std::string, data> Decode(const std::string& str) {
|
||||
bool lower = false, upper = false;
|
||||
for (size_t i = 0; i < str.size(); ++i) {
|
||||
unsigned char c = str[i];
|
||||
if (c < 33 || c > 126) return {};
|
||||
if (c >= 'a' && c <= 'z') lower = true;
|
||||
if (c >= 'A' && c <= 'Z') upper = true;
|
||||
}
|
||||
if (lower && upper) return {};
|
||||
size_t pos = str.rfind('1');
|
||||
if (str.size() > 1023 || pos == str.npos || pos == 0 || pos + 7 > str.size()) {
|
||||
return {};
|
||||
}
|
||||
data values(str.size() - 1 - pos);
|
||||
for (size_t i = 0; i < str.size() - 1 - pos; ++i) {
|
||||
unsigned char c = str[i + pos + 1];
|
||||
int8_t rev = (c < 33 || c > 126) ? -1 : CHARSET_REV[c];
|
||||
if (rev == -1) {
|
||||
return {};
|
||||
}
|
||||
values[i] = rev;
|
||||
}
|
||||
std::string hrp;
|
||||
for (size_t i = 0; i < pos; ++i) {
|
||||
hrp += LowerCase(str[i]);
|
||||
}
|
||||
if (!VerifyChecksum(hrp, values)) {
|
||||
return {};
|
||||
}
|
||||
return {hrp, data(values.begin(), values.end() - 6)};
|
||||
}
|
||||
|
||||
} // namespace bech32
|
||||
30
src/bech32.h
Normal file
30
src/bech32.h
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) 2017 Pieter Wuille
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
// Bech32 is a string encoding format used in newer address types.
|
||||
// The output consists of a human-readable part (alphanumeric), a
|
||||
// separator character (1), and a base32 data section, the last
|
||||
// 6 characters of which are a checksum.
|
||||
//
|
||||
// For more information, see BIP 173.
|
||||
|
||||
#ifndef BITCOIN_BECH32_H
|
||||
#define BITCOIN_BECH32_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace bech32
|
||||
{
|
||||
|
||||
/** Encode a Bech32 string. Returns the empty string in case of failure. */
|
||||
std::string Encode(const std::string& hrp, const std::vector<uint8_t>& values);
|
||||
|
||||
/** Decode a Bech32 string. Returns (hrp, data). Empty hrp means failure. */
|
||||
std::pair<std::string, std::vector<uint8_t>> Decode(const std::string& str);
|
||||
|
||||
} // namespace bech32
|
||||
|
||||
#endif // BITCOIN_BECH32_H
|
||||
@@ -5,8 +5,8 @@
|
||||
|
||||
#include "chainparamsbase.h"
|
||||
#include "clientversion.h"
|
||||
#include "rpcclient.h"
|
||||
#include "rpcprotocol.h"
|
||||
#include "rpc/client.h"
|
||||
#include "rpc/protocol.h"
|
||||
#include "util.h"
|
||||
#include "utilstrencodings.h"
|
||||
|
||||
@@ -21,14 +21,12 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
int64_t MAX_MONEY = 200000000 * 100000000LL;
|
||||
uint64_t komodo_maxallowed(int32_t baseid) { return(100000000LL * 1000000); } // stub
|
||||
|
||||
static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900;
|
||||
static const int CONTINUE_EXECUTION=-1;
|
||||
|
||||
std::string HelpMessageCli()
|
||||
{
|
||||
string strUsage;
|
||||
std::string strUsage;
|
||||
strUsage += HelpMessageGroup(_("Options:"));
|
||||
strUsage += HelpMessageOpt("-?", _("This help message"));
|
||||
strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), "komodo.conf"));
|
||||
@@ -42,6 +40,7 @@ std::string HelpMessageCli()
|
||||
strUsage += HelpMessageOpt("-rpcuser=<user>", _("Username for JSON-RPC connections"));
|
||||
strUsage += HelpMessageOpt("-rpcpassword=<pw>", _("Password for JSON-RPC connections"));
|
||||
strUsage += HelpMessageOpt("-rpcclienttimeout=<n>", strprintf(_("Timeout in seconds during HTTP requests, or 0 for no timeout. (default: %d)"), DEFAULT_HTTP_CLIENT_TIMEOUT));
|
||||
strUsage += HelpMessageOpt("-stdin", _("Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases)"));
|
||||
|
||||
return strUsage;
|
||||
}
|
||||
@@ -86,8 +85,16 @@ uint32_t komodo_heightstamp(int32_t height)
|
||||
return(0);
|
||||
}
|
||||
|
||||
static bool AppInitRPC(int argc, char* argv[])
|
||||
//
|
||||
// This function returns either one of EXIT_ codes when it's expected to stop the process or
|
||||
// CONTINUE_EXECUTION when it's expected to continue further.
|
||||
//
|
||||
static int AppInitRPC(int argc, char* argv[])
|
||||
{
|
||||
static_assert(CONTINUE_EXECUTION != EXIT_FAILURE,
|
||||
"CONTINUE_EXECUTION should be different from EXIT_FAILURE");
|
||||
static_assert(CONTINUE_EXECUTION != EXIT_SUCCESS,
|
||||
"CONTINUE_EXECUTION should be different from EXIT_SUCCESS");
|
||||
//
|
||||
// Parameters
|
||||
//
|
||||
@@ -107,29 +114,33 @@ static bool AppInitRPC(int argc, char* argv[])
|
||||
}
|
||||
|
||||
fprintf(stdout, "%s", strUsage.c_str());
|
||||
return false;
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "Error: too few parameters\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
if (!boost::filesystem::is_directory(GetDataDir(false))) {
|
||||
fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", mapArgs["-datadir"].c_str());
|
||||
return false;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
try {
|
||||
ReadConfigFile(mapArgs, mapMultiArgs);
|
||||
} catch (const std::exception& e) {
|
||||
fprintf(stderr,"Error reading configuration file: %s\n", e.what());
|
||||
return false;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
// Check for -testnet or -regtest parameter (BaseParams() calls are only valid after this clause)
|
||||
if (!SelectBaseParamsFromCommandLine()) {
|
||||
fprintf(stderr, "Error: Invalid combination of -regtest and -testnet.\n");
|
||||
return false;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (GetBoolArg("-rpcssl", false))
|
||||
{
|
||||
fprintf(stderr, "Error: SSL mode for RPC (-rpcssl) is no longer supported.\n");
|
||||
return false;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return true;
|
||||
return CONTINUE_EXECUTION;
|
||||
}
|
||||
|
||||
|
||||
@@ -198,7 +209,7 @@ static void http_error_cb(enum evhttp_request_error err, void *ctx)
|
||||
}
|
||||
#endif
|
||||
|
||||
UniValue CallRPC(const string& strMethod, const UniValue& params)
|
||||
UniValue CallRPC(const std::string& strMethod, const UniValue& params)
|
||||
{
|
||||
std::string host = GetArg("-rpcconnect", "127.0.0.1");
|
||||
int port = GetArg("-rpcport", BaseParams().RPCPort());
|
||||
@@ -213,7 +224,7 @@ UniValue CallRPC(const string& strMethod, const UniValue& params)
|
||||
HTTPReply response;
|
||||
raii_evhttp_request req = obtain_evhttp_request(http_request_done, (void*)&response);
|
||||
if (req == NULL)
|
||||
throw runtime_error("create http request failed");
|
||||
throw std::runtime_error("create http request failed");
|
||||
#if LIBEVENT_VERSION_NUMBER >= 0x02010300
|
||||
evhttp_request_set_error_cb(req.get(), http_error_cb);
|
||||
#endif
|
||||
@@ -223,7 +234,7 @@ UniValue CallRPC(const string& strMethod, const UniValue& params)
|
||||
if (mapArgs["-rpcpassword"] == "") {
|
||||
// Try fall back to cookie-based authentication if no password is provided
|
||||
if (!GetAuthCookie(&strRPCUserColonPass)) {
|
||||
throw runtime_error(strprintf(
|
||||
throw std::runtime_error(strprintf(
|
||||
_("Could not locate RPC credentials. No authentication cookie could be found,\n"
|
||||
"and no rpcpassword is set in the configuration file (%s)."),
|
||||
GetConfigFile().string().c_str()));
|
||||
@@ -256,26 +267,26 @@ UniValue CallRPC(const string& strMethod, const UniValue& params)
|
||||
if (response.status == 0)
|
||||
throw CConnectionFailed(strprintf("couldn't connect to server: %s (code %d)\n(make sure server is running and you are connecting to the correct RPC port)", http_errorstring(response.error), response.error));
|
||||
else if (response.status == HTTP_UNAUTHORIZED)
|
||||
throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
|
||||
throw std::runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
|
||||
else if (response.status >= 400 && response.status != HTTP_BAD_REQUEST && response.status != HTTP_NOT_FOUND && response.status != HTTP_INTERNAL_SERVER_ERROR)
|
||||
throw runtime_error(strprintf("server returned HTTP error %d", response.status));
|
||||
throw std::runtime_error(strprintf("server returned HTTP error %d", response.status));
|
||||
else if (response.body.empty())
|
||||
throw runtime_error("no response from server");
|
||||
throw std::runtime_error("no response from server");
|
||||
|
||||
// Parse reply
|
||||
UniValue valReply(UniValue::VSTR);
|
||||
if (!valReply.read(response.body))
|
||||
throw runtime_error("couldn't parse reply from server");
|
||||
throw std::runtime_error("couldn't parse reply from server");
|
||||
const UniValue& reply = valReply.get_obj();
|
||||
if (reply.empty())
|
||||
throw runtime_error("expected reply to have result, error and id properties");
|
||||
throw std::runtime_error("expected reply to have result, error and id properties");
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
int CommandLineRPC(int argc, char *argv[])
|
||||
{
|
||||
string strPrint;
|
||||
std::string strPrint;
|
||||
int nRet = 0;
|
||||
try {
|
||||
// Skip switches
|
||||
@@ -283,15 +294,17 @@ int CommandLineRPC(int argc, char *argv[])
|
||||
argc--;
|
||||
argv++;
|
||||
}
|
||||
|
||||
// Method
|
||||
if (argc < 2)
|
||||
throw runtime_error("too few parameters");
|
||||
string strMethod = argv[1];
|
||||
|
||||
// Parameters default to strings
|
||||
std::vector<std::string> strParams(&argv[2], &argv[argc]);
|
||||
UniValue params = RPCConvertValues(strMethod, strParams);
|
||||
std::vector<std::string> args = std::vector<std::string>(&argv[1], &argv[argc]);
|
||||
if (GetBoolArg("-stdin", false)) {
|
||||
// Read one arg per line from stdin and append
|
||||
std::string line;
|
||||
while (std::getline(std::cin,line))
|
||||
args.push_back(line);
|
||||
}
|
||||
if (args.size() < 1)
|
||||
throw std::runtime_error("too few parameters (need at least command)");
|
||||
std::string strMethod = args[0];
|
||||
UniValue params = RPCConvertValues(strMethod, std::vector<std::string>(args.begin()+1, args.end()));
|
||||
|
||||
// Execute and handle connection failures with -rpcwait
|
||||
const bool fWait = GetBoolArg("-rpcwait", false);
|
||||
@@ -343,7 +356,7 @@ int CommandLineRPC(int argc, char *argv[])
|
||||
throw;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
strPrint = string("error: ") + e.what();
|
||||
strPrint = std::string("error: ") + e.what();
|
||||
nRet = EXIT_FAILURE;
|
||||
}
|
||||
catch (...) {
|
||||
@@ -362,12 +375,13 @@ int main(int argc, char* argv[])
|
||||
SetupEnvironment();
|
||||
if (!SetupNetworking()) {
|
||||
fprintf(stderr, "Error: Initializing networking failed\n");
|
||||
exit(1);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
try {
|
||||
if(!AppInitRPC(argc, argv))
|
||||
return EXIT_FAILURE;
|
||||
int ret = AppInitRPC(argc, argv);
|
||||
if (ret != CONTINUE_EXECUTION)
|
||||
return ret;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
PrintExceptionContinue(&e, "AppInitRPC()");
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "clientversion.h"
|
||||
#include "rpcserver.h"
|
||||
#include "rpc/server.h"
|
||||
#include "init.h"
|
||||
#include "main.h"
|
||||
#include "noui.h"
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "util.h"
|
||||
#include "httpserver.h"
|
||||
#include "httprpc.h"
|
||||
#include "rpcserver.h"
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
@@ -115,7 +114,7 @@ bool AppInit(int argc, char* argv[])
|
||||
}
|
||||
|
||||
fprintf(stdout, "%s", strUsage.c_str());
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
try
|
||||
@@ -179,7 +178,7 @@ bool AppInit(int argc, char* argv[])
|
||||
if (fCommandLine)
|
||||
{
|
||||
fprintf(stderr, "Error: There is no RPC client functionality in komodod. Use the komodo-cli utility instead.\n");
|
||||
exit(1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
@@ -236,5 +235,5 @@ int main(int argc, char* argv[])
|
||||
// Connect bitcoind signal handlers
|
||||
noui_connect();
|
||||
|
||||
return (AppInit(argc, argv) ? 0 : 1);
|
||||
return (AppInit(argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ public:
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(vData);
|
||||
READWRITE(nHashFuncs);
|
||||
READWRITE(nTweak);
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
******************************************************************************/
|
||||
|
||||
#include "CCinclude.h"
|
||||
#include "key_io.h"
|
||||
|
||||
/*
|
||||
FinalizeCCTx is a very useful function that will properly sign both CC and normal inputs, adds normal change and the opreturn.
|
||||
|
||||
@@ -29,7 +29,7 @@ public:
|
||||
uint256 notarisationHash;
|
||||
ADD_SERIALIZE_METHODS;
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(branch);
|
||||
READWRITE(notarisationHash);
|
||||
}
|
||||
|
||||
@@ -170,10 +170,9 @@ CPubKey DiceFundingPk(CScript scriptPubKey)
|
||||
CPubKey pk; uint8_t *ptr,*dest; int32_t i;
|
||||
if ( scriptPubKey.size() == 35 )
|
||||
{
|
||||
ptr = (uint8_t *)scriptPubKey.data();
|
||||
dest = (uint8_t *)pk.begin();
|
||||
for (i=0; i<33; i++)
|
||||
dest[i] = ptr[i+1];
|
||||
dest[i] = scriptPubKey[i+1];
|
||||
} else fprintf(stderr,"DiceFundingPk invalid size.%d\n",(int32_t)scriptPubKey.size());
|
||||
return(pk);
|
||||
}
|
||||
@@ -514,13 +513,12 @@ bool DiceValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx)
|
||||
fprintf(stderr,"bidTx.%s\n",uint256_str(str,txid));
|
||||
fprintf(stderr,"entropyTx.%s v%d\n",uint256_str(str,tx.vin[0].prevout.hash),(int32_t)tx.vin[0].prevout.n);
|
||||
fprintf(stderr,"entropyTx vin0 %s v%d\n",uint256_str(str,vinTx.vin[0].prevout.hash),(int32_t)vinTx.vin[0].prevout.n);
|
||||
ptr0 = (uint8_t *)vinofvinTx.vout[vinTx.vin[0].prevout.n].scriptPubKey.data();
|
||||
ptr1 = (uint8_t *)fundingPubKey.data();
|
||||
const CScript &s0 = vinofvinTx.vout[vinTx.vin[0].prevout.n].scriptPubKey;
|
||||
for (i=0; i<vinofvinTx.vout[vinTx.vin[0].prevout.n].scriptPubKey.size(); i++)
|
||||
fprintf(stderr,"%02x",ptr0[i]);
|
||||
fprintf(stderr,"%02x",s0[i]);
|
||||
fprintf(stderr," script vs ");
|
||||
for (i=0; i<fundingPubKey.size(); i++)
|
||||
fprintf(stderr,"%02x",ptr1[i]);
|
||||
fprintf(stderr,"%02x",fundingPubKey[i]);
|
||||
fprintf(stderr," (%c) entropy vin.%d fundingPubKey mismatch %s\n",funcid,vinTx.vin[0].prevout.n,uint256_str(str,vinTx.vin[0].prevout.hash));
|
||||
return eval->Invalid("vin1 of entropy tx not fundingPubKey for bet");
|
||||
}
|
||||
@@ -562,7 +560,7 @@ bool DiceValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx)
|
||||
return eval->Invalid("vout[0] != inputs-txfee for loss");
|
||||
else if ( tx.vout[2].scriptPubKey != fundingPubKey )
|
||||
{
|
||||
if ( tx.vout[2].scriptPubKey.size() == 0 || ((uint8_t *)tx.vout[2].scriptPubKey.data())[0] != 0x6a )
|
||||
if ( tx.vout[2].scriptPubKey.size() == 0 || tx.vout[2].scriptPubKey[0] != 0x6a )
|
||||
return eval->Invalid("vout[2] not send to fundingPubKey for loss");
|
||||
}
|
||||
iswin = -1;
|
||||
@@ -586,7 +584,7 @@ bool DiceValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx)
|
||||
}
|
||||
else if ( tx.vout[3].scriptPubKey != fundingPubKey )
|
||||
{
|
||||
if ( tx.vout[3].scriptPubKey.size() == 0 || ((uint8_t *)tx.vout[3].scriptPubKey.data())[0] != 0x6a )
|
||||
if ( tx.vout[3].scriptPubKey.size() == 0 || tx.vout[3].scriptPubKey[0] != 0x6a )
|
||||
return eval->Invalid("vout[3] not send to fundingPubKey for win/timeout");
|
||||
}
|
||||
iswin = (funcid == 'W');
|
||||
@@ -700,13 +698,12 @@ int64_t DicePlanFunds(uint64_t &entropyval,uint256 &entropytxid,uint64_t refsbit
|
||||
if ( vinTx.vout[tx.vin[0].prevout.n].scriptPubKey != fundingPubKey )
|
||||
{
|
||||
uint8_t *ptr0,*ptr1; int32_t i; char str[65];
|
||||
ptr0 = (uint8_t *)vinTx.vout[tx.vin[0].prevout.n].scriptPubKey.data();
|
||||
ptr1 = (uint8_t *)fundingPubKey.data();
|
||||
const CScript &s0 = vinTx.vout[tx.vin[0].prevout.n].scriptPubKey;
|
||||
for (i=0; i<vinTx.vout[tx.vin[0].prevout.n].scriptPubKey.size(); i++)
|
||||
fprintf(stderr,"%02x",ptr0[i]);
|
||||
fprintf(stderr,"%02x",s0[i]);
|
||||
fprintf(stderr," script vs ");
|
||||
for (i=0; i<fundingPubKey.size(); i++)
|
||||
fprintf(stderr,"%02x",ptr1[i]);
|
||||
fprintf(stderr,"%02x",fundingPubKey[i]);
|
||||
fprintf(stderr," (%c) entropy vin.%d fundingPubKey mismatch %s\n",funcid,tx.vin[0].prevout.n,uint256_str(str,tx.vin[0].prevout.hash));
|
||||
continue;
|
||||
}
|
||||
@@ -718,13 +715,12 @@ int64_t DicePlanFunds(uint64_t &entropyval,uint256 &entropytxid,uint64_t refsbit
|
||||
else
|
||||
{
|
||||
uint8_t *ptr0,*ptr1; int32_t i; char str[65];
|
||||
ptr0 = (uint8_t *)tx.vout[1].scriptPubKey.data();
|
||||
ptr1 = (uint8_t *)fundingPubKey.data();
|
||||
const CScript &s0 = tx.vout[1].scriptPubKey;
|
||||
for (i=0; i<tx.vout[1].scriptPubKey.size(); i++)
|
||||
fprintf(stderr,"%02x",ptr0[i]);
|
||||
fprintf(stderr,"%02x",s0[i]);
|
||||
fprintf(stderr," script vs ");
|
||||
for (i=0; i<fundingPubKey.size(); i++)
|
||||
fprintf(stderr,"%02x",ptr1[i]);
|
||||
fprintf(stderr,"%02x",fundingPubKey[i]);
|
||||
fprintf(stderr," (%c) tx vin.%d fundingPubKey mismatch %s\n",funcid,tx.vin[0].prevout.n,uint256_str(str,tx.vin[0].prevout.hash));
|
||||
}
|
||||
}
|
||||
@@ -900,13 +896,11 @@ std::string DiceAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,int6
|
||||
if ( 0 )
|
||||
{
|
||||
uint8_t *ptr0,*ptr1; int32_t i;
|
||||
ptr0 = (uint8_t *)scriptPubKey.data();
|
||||
ptr1 = (uint8_t *)fundingPubKey.data();
|
||||
for (i=0; i<35; i++)
|
||||
fprintf(stderr,"%02x",ptr0[i]);
|
||||
fprintf(stderr,"%02x",scriptPubKey[i]);
|
||||
fprintf(stderr," script vs ");
|
||||
for (i=0; i<35; i++)
|
||||
fprintf(stderr,"%02x",ptr1[i]);
|
||||
fprintf(stderr,"%02x",fundingPubKey[i]);
|
||||
fprintf(stderr," funding\n");
|
||||
}
|
||||
if ( scriptPubKey == fundingPubKey )
|
||||
|
||||
@@ -161,7 +161,8 @@ bool Eval::CheckNotaryInputs(const CTransaction &tx, uint32_t height, uint32_t t
|
||||
if (tx.vout.size() < txIn.prevout.n) return false;
|
||||
CScript spk = tx.vout[txIn.prevout.n].scriptPubKey;
|
||||
if (spk.size() != 35) return false;
|
||||
const unsigned char *pk = spk.data();
|
||||
std::vector<unsigned char> scriptVec = std::vector<unsigned char>(spk.begin(),spk.end());
|
||||
const unsigned char *pk = scriptVec.data();
|
||||
if (pk++[0] != 33) return false;
|
||||
if (pk[33] != OP_CHECKSIG) return false;
|
||||
|
||||
|
||||
@@ -174,7 +174,7 @@ public:
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
|
||||
bool IsBack = IsBackNotarisation;
|
||||
if (2 == IsBackNotarisation) IsBack = DetectBackNotarisation(s, ser_action);
|
||||
@@ -270,7 +270,7 @@ public:
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(VARINT(nIndex));
|
||||
READWRITE(branch);
|
||||
}
|
||||
|
||||
@@ -94,6 +94,7 @@ CBlockIndex* CBlockIndex::GetAncestor(int height)
|
||||
pindexWalk = pindexWalk->pskip;
|
||||
heightWalk = heightSkip;
|
||||
} else {
|
||||
assert(pindexWalk->pprev);
|
||||
pindexWalk = pindexWalk->pprev;
|
||||
heightWalk--;
|
||||
}
|
||||
|
||||
49
src/chain.h
49
src/chain.h
@@ -17,6 +17,7 @@
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
static const int SPROUT_VALUE_VERSION = 1001400;
|
||||
static const int SAPLING_VALUE_VERSION = 1010100;
|
||||
|
||||
struct CDiskBlockPos
|
||||
{
|
||||
@@ -26,7 +27,7 @@ struct CDiskBlockPos
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(VARINT(nFile));
|
||||
READWRITE(VARINT(nPos));
|
||||
}
|
||||
@@ -152,10 +153,10 @@ public:
|
||||
boost::optional<uint32_t> nCachedBranchId;
|
||||
|
||||
//! The anchor for the tree state up to the start of this block
|
||||
uint256 hashAnchor;
|
||||
uint256 hashSproutAnchor;
|
||||
|
||||
//! (memory only) The anchor for the tree state up to the end of this block
|
||||
uint256 hashAnchorEnd;
|
||||
uint256 hashFinalSproutRoot;
|
||||
|
||||
//! Change in value held by the Sprout circuit over this block.
|
||||
//! Will be boost::none for older blocks on old nodes until a reindex has taken place.
|
||||
@@ -166,10 +167,19 @@ public:
|
||||
//! Will be boost::none if nChainTx is zero.
|
||||
boost::optional<CAmount> nChainSproutValue;
|
||||
|
||||
//! Change in value held by the Sapling circuit over this block.
|
||||
//! Not a boost::optional because this was added before Sapling activated, so we can
|
||||
//! rely on the invariant that every block before this was added had nSaplingValue = 0.
|
||||
CAmount nSaplingValue;
|
||||
|
||||
//! (memory only) Total value held by the Sapling circuit up to and including this block.
|
||||
//! Will be boost::none if nChainTx is zero.
|
||||
boost::optional<CAmount> nChainSaplingValue;
|
||||
|
||||
//! block header
|
||||
int nVersion;
|
||||
uint256 hashMerkleRoot;
|
||||
uint256 hashReserved;
|
||||
uint256 hashFinalSaplingRoot;
|
||||
unsigned int nTime;
|
||||
unsigned int nBits;
|
||||
uint256 nNonce;
|
||||
@@ -194,15 +204,17 @@ public:
|
||||
nChainTx = 0;
|
||||
nStatus = 0;
|
||||
nCachedBranchId = boost::none;
|
||||
hashAnchor = uint256();
|
||||
hashAnchorEnd = uint256();
|
||||
hashSproutAnchor = uint256();
|
||||
hashFinalSproutRoot = uint256();
|
||||
nSequenceId = 0;
|
||||
nSproutValue = boost::none;
|
||||
nChainSproutValue = boost::none;
|
||||
nSaplingValue = 0;
|
||||
nChainSaplingValue = boost::none;
|
||||
|
||||
nVersion = 0;
|
||||
hashMerkleRoot = uint256();
|
||||
hashReserved = uint256();
|
||||
hashFinalSaplingRoot = uint256();
|
||||
nTime = 0;
|
||||
nBits = 0;
|
||||
nNonce = uint256();
|
||||
@@ -220,7 +232,7 @@ public:
|
||||
|
||||
nVersion = block.nVersion;
|
||||
hashMerkleRoot = block.hashMerkleRoot;
|
||||
hashReserved = block.hashReserved;
|
||||
hashFinalSaplingRoot = block.hashFinalSaplingRoot;
|
||||
nTime = block.nTime;
|
||||
nBits = block.nBits;
|
||||
nNonce = block.nNonce;
|
||||
@@ -252,7 +264,7 @@ public:
|
||||
if (pprev)
|
||||
block.hashPrevBlock = pprev->GetBlockHash();
|
||||
block.hashMerkleRoot = hashMerkleRoot;
|
||||
block.hashReserved = hashReserved;
|
||||
block.hashFinalSaplingRoot = hashFinalSaplingRoot;
|
||||
block.nTime = nTime;
|
||||
block.nBits = nBits;
|
||||
block.nNonce = nNonce;
|
||||
@@ -352,8 +364,9 @@ public:
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
if (!(nType & SER_GETHASH))
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
int nVersion = s.GetVersion();
|
||||
if (!(s.GetType() & SER_GETHASH))
|
||||
READWRITE(VARINT(nVersion));
|
||||
|
||||
READWRITE(VARINT(nHeight));
|
||||
@@ -377,13 +390,13 @@ public:
|
||||
READWRITE(branchId);
|
||||
}
|
||||
}
|
||||
READWRITE(hashAnchor);
|
||||
READWRITE(hashSproutAnchor);
|
||||
|
||||
// block header
|
||||
READWRITE(this->nVersion);
|
||||
READWRITE(hashPrev);
|
||||
READWRITE(hashMerkleRoot);
|
||||
READWRITE(hashReserved);
|
||||
READWRITE(hashFinalSaplingRoot);
|
||||
READWRITE(nTime);
|
||||
READWRITE(nBits);
|
||||
READWRITE(nNonce);
|
||||
@@ -391,9 +404,15 @@ public:
|
||||
|
||||
// Only read/write nSproutValue if the client version used to create
|
||||
// this index was storing them.
|
||||
if ((nType & SER_DISK) && (nVersion >= SPROUT_VALUE_VERSION)) {
|
||||
if ((s.GetType() & SER_DISK) && (nVersion >= SPROUT_VALUE_VERSION)) {
|
||||
READWRITE(nSproutValue);
|
||||
}
|
||||
|
||||
// Only read/write nSaplingValue if the client version used to create
|
||||
// this index was storing them.
|
||||
if ((s.GetType() & SER_DISK) && (nVersion >= SAPLING_VALUE_VERSION)) {
|
||||
READWRITE(nSaplingValue);
|
||||
}
|
||||
}
|
||||
|
||||
uint256 GetBlockHash() const
|
||||
@@ -402,7 +421,7 @@ public:
|
||||
block.nVersion = nVersion;
|
||||
block.hashPrevBlock = hashPrev;
|
||||
block.hashMerkleRoot = hashMerkleRoot;
|
||||
block.hashReserved = hashReserved;
|
||||
block.hashFinalSaplingRoot = hashFinalSaplingRoot;
|
||||
block.nTime = nTime;
|
||||
block.nBits = nBits;
|
||||
block.nNonce = nNonce;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "key_io.h"
|
||||
#include "main.h"
|
||||
#include "crypto/equihash.h"
|
||||
|
||||
@@ -13,14 +14,12 @@
|
||||
|
||||
#include <boost/assign/list_of.hpp>
|
||||
|
||||
#include "base58.h"
|
||||
|
||||
#include "chainparamsseeds.h"
|
||||
|
||||
static CBlock CreateGenesisBlock(const char* pszTimestamp, const CScript& genesisOutputScript, uint32_t nTime, const uint256& nNonce, const std::vector<unsigned char>& nSolution, uint32_t nBits, int32_t nVersion, const CAmount& genesisReward)
|
||||
{
|
||||
// To create a genesis block for a new chain which is Overwintered:
|
||||
// txNew.nVersion = 3
|
||||
// txNew.nVersion = OVERWINTER_TX_VERSION
|
||||
// txNew.fOverwintered = true
|
||||
// txNew.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID
|
||||
// txNew.nExpiryHeight = <default value>
|
||||
@@ -92,6 +91,7 @@ public:
|
||||
{
|
||||
strNetworkID = "main";
|
||||
strCurrencyUnits = "KMD";
|
||||
bip44CoinType = 133; // As registered in https://github.com/satoshilabs/slips/blob/master/slip-0044.md (ZCASH, should be VRSC)
|
||||
consensus.fCoinbaseMustBeProtected = false; // true this is only true wuth Verus and enforced after block 12800
|
||||
consensus.nSubsidySlowStartInterval = 20000;
|
||||
consensus.nSubsidyHalvingInterval = 840000;
|
||||
@@ -114,9 +114,13 @@ public:
|
||||
consensus.vUpgrades[Consensus::UPGRADE_TESTDUMMY].nProtocolVersion = 170002;
|
||||
consensus.vUpgrades[Consensus::UPGRADE_TESTDUMMY].nActivationHeight =
|
||||
Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
|
||||
consensus.vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion = 170004;
|
||||
consensus.vUpgrades[Consensus::UPGRADE_OVERWINTER].nActivationHeight =
|
||||
Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
|
||||
consensus.vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion = 170005;
|
||||
consensus.vUpgrades[Consensus::UPGRADE_OVERWINTER].nActivationHeight = 227520;
|
||||
consensus.vUpgrades[Consensus::UPGRADE_SAPLING].nProtocolVersion = 170007;
|
||||
consensus.vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight = 227520;
|
||||
|
||||
// The best chain should have at least this much work.
|
||||
consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000000000000000281b32ff3198a1");
|
||||
|
||||
/**
|
||||
* The message start string is designed to be unlikely to occur in normal data.
|
||||
@@ -128,6 +132,7 @@ public:
|
||||
pchMessageStart[2] = 0xe4;
|
||||
pchMessageStart[3] = 0x8d;
|
||||
vAlertPubKey = ParseHex("020e46e79a2a8d12b9b5d12c7a91adb4e454edfae43c0a0cb805427d2ac7613fd9");
|
||||
// (Zcash) vAlertPubKey = ParseHex("04b7ecf0baa90495ceb4e4090f6b2fd37eec1e9c85fac68a487f3ce11589692e4a317479316ee814e066638e1db54e37a10689b70286e6315b1087b6615d179264");
|
||||
nDefaultPort = 7770;
|
||||
nMinerThreads = 0;
|
||||
nMaxTipAge = 24 * 60 * 60;
|
||||
@@ -182,6 +187,11 @@ public:
|
||||
// guarantees the first two characters, when base58 encoded, are "SK"
|
||||
base58Prefixes[ZCSPENDING_KEY] = {171,54};
|
||||
|
||||
bech32HRPs[SAPLING_PAYMENT_ADDRESS] = "zs";
|
||||
bech32HRPs[SAPLING_FULL_VIEWING_KEY] = "zviews";
|
||||
bech32HRPs[SAPLING_INCOMING_VIEWING_KEY] = "zivks";
|
||||
bech32HRPs[SAPLING_EXTENDED_SPEND_KEY] = "secret-extended-key-main";
|
||||
|
||||
vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_main, pnSeed6_main + ARRAYLEN(pnSeed6_main));
|
||||
|
||||
fMiningRequiresPeers = true;
|
||||
@@ -455,6 +465,7 @@ public:
|
||||
CTestNetParams() {
|
||||
strNetworkID = "test";
|
||||
strCurrencyUnits = "TAZ";
|
||||
bip44CoinType = 1;
|
||||
consensus.fCoinbaseMustBeProtected = true;
|
||||
consensus.nSubsidySlowStartInterval = 20000;
|
||||
consensus.nSubsidyHalvingInterval = 840000;
|
||||
@@ -481,6 +492,11 @@ public:
|
||||
Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
|
||||
consensus.vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion = 170003;
|
||||
consensus.vUpgrades[Consensus::UPGRADE_OVERWINTER].nActivationHeight = 207500;
|
||||
consensus.vUpgrades[Consensus::UPGRADE_SAPLING].nProtocolVersion = 170007;
|
||||
consensus.vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight = 280000;
|
||||
|
||||
// The best chain should have at least this much work.
|
||||
consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000000000000000000001d0c4d9cd");
|
||||
|
||||
consensus.fPowAllowMinDifficultyBlocks = true;
|
||||
pchMessageStart[0] = 0x5A;
|
||||
@@ -518,6 +534,11 @@ public:
|
||||
base58Prefixes[ZCVIEWING_KEY] = {0xA8,0xAC,0x0C};
|
||||
base58Prefixes[ZCSPENDING_KEY] = {177,235};
|
||||
|
||||
bech32HRPs[SAPLING_PAYMENT_ADDRESS] = "ztestsapling";
|
||||
bech32HRPs[SAPLING_FULL_VIEWING_KEY] = "zviewtestsapling";
|
||||
bech32HRPs[SAPLING_INCOMING_VIEWING_KEY] = "zivktestsapling";
|
||||
bech32HRPs[SAPLING_EXTENDED_SPEND_KEY] = "secret-extended-key-test";
|
||||
|
||||
vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_test, pnSeed6_test + ARRAYLEN(pnSeed6_test));
|
||||
|
||||
//fRequireRPCPassword = true;
|
||||
@@ -527,6 +548,7 @@ public:
|
||||
fMineBlocksOnDemand = false;
|
||||
fTestnetToBeDeprecatedFieldRPC = true;
|
||||
|
||||
|
||||
checkpointData = (CCheckpointData) {
|
||||
boost::assign::map_list_of
|
||||
(0, consensus.hashGenesisBlock)
|
||||
@@ -548,6 +570,7 @@ public:
|
||||
CRegTestParams() {
|
||||
strNetworkID = "regtest";
|
||||
strCurrencyUnits = "REG";
|
||||
bip44CoinType = 1;
|
||||
consensus.fCoinbaseMustBeProtected = false;
|
||||
consensus.nSubsidySlowStartInterval = 0;
|
||||
consensus.nSubsidyHalvingInterval = 150;
|
||||
@@ -571,6 +594,12 @@ public:
|
||||
consensus.vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion = 170003;
|
||||
consensus.vUpgrades[Consensus::UPGRADE_OVERWINTER].nActivationHeight =
|
||||
Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
|
||||
consensus.vUpgrades[Consensus::UPGRADE_SAPLING].nProtocolVersion = 170006;
|
||||
consensus.vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight =
|
||||
Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
|
||||
|
||||
// The best chain should have at least this much work.
|
||||
consensus.nMinimumChainWork = uint256S("0x00");
|
||||
|
||||
pchMessageStart[0] = 0xaa;
|
||||
pchMessageStart[1] = 0x8e;
|
||||
@@ -628,6 +657,11 @@ public:
|
||||
base58Prefixes[ZCVIEWING_KEY] = {0xA8,0xAC,0x0C};
|
||||
base58Prefixes[ZCSPENDING_KEY] = {0xAC,0x08};
|
||||
|
||||
bech32HRPs[SAPLING_PAYMENT_ADDRESS] = "zregtestsapling";
|
||||
bech32HRPs[SAPLING_FULL_VIEWING_KEY] = "zviewregtestsapling";
|
||||
bech32HRPs[SAPLING_INCOMING_VIEWING_KEY] = "zivkregtestsapling";
|
||||
bech32HRPs[SAPLING_EXTENDED_SPEND_KEY] = "secret-extended-key-regtest";
|
||||
|
||||
// Founders reward script expects a vector of 2-of-3 multisig addresses
|
||||
vFoundersRewardAddress = { "t2FwcEhFdNXuFMv1tcYwaBJtYVtMj8b1uTg" };
|
||||
assert(vFoundersRewardAddress.size() <= consensus.GetLastFoundersRewardBlockHeight());
|
||||
@@ -699,10 +733,10 @@ std::string CChainParams::GetFoundersRewardAddressAtHeight(int nHeight) const {
|
||||
CScript CChainParams::GetFoundersRewardScriptAtHeight(int nHeight) const {
|
||||
assert(nHeight > 0 && nHeight <= consensus.GetLastFoundersRewardBlockHeight());
|
||||
|
||||
CBitcoinAddress address(GetFoundersRewardAddressAtHeight(nHeight).c_str());
|
||||
assert(address.IsValid());
|
||||
assert(address.IsScript());
|
||||
CScriptID scriptID = boost::get<CScriptID>(address.Get()); // Get() returns a boost variant
|
||||
CTxDestination address = DecodeDestination(GetFoundersRewardAddressAtHeight(nHeight).c_str());
|
||||
assert(IsValidDestination(address));
|
||||
assert(boost::get<CScriptID>(&address) != nullptr);
|
||||
CScriptID scriptID = boost::get<CScriptID>(address); // address is a boost variant
|
||||
CScript script = CScript() << OP_HASH160 << ToByteVector(scriptID) << OP_EQUAL;
|
||||
return script;
|
||||
}
|
||||
|
||||
@@ -58,6 +58,15 @@ public:
|
||||
double fTransactionsPerDay;
|
||||
};
|
||||
|
||||
enum Bech32Type {
|
||||
SAPLING_PAYMENT_ADDRESS,
|
||||
SAPLING_FULL_VIEWING_KEY,
|
||||
SAPLING_INCOMING_VIEWING_KEY,
|
||||
SAPLING_EXTENDED_SPEND_KEY,
|
||||
|
||||
MAX_BECH32_TYPES
|
||||
};
|
||||
|
||||
const Consensus::Params& GetConsensus() const { return consensus; }
|
||||
const CMessageHeader::MessageStartChars& MessageStart() const { return pchMessageStart; }
|
||||
const std::vector<unsigned char>& AlertKey() const { return vAlertPubKey; }
|
||||
@@ -70,11 +79,11 @@ public:
|
||||
bool DefaultConsistencyChecks() const { return fDefaultConsistencyChecks; }
|
||||
/** Policy: Filter transactions that do not match well-defined patterns */
|
||||
bool RequireStandard() const { return fRequireStandard; }
|
||||
int64_t MaxTipAge() const { return nMaxTipAge; }
|
||||
int64_t PruneAfterHeight() const { return nPruneAfterHeight; }
|
||||
unsigned int EquihashN() const { return nEquihashN; }
|
||||
unsigned int EquihashK() const { return nEquihashK; }
|
||||
std::string CurrencyUnits() const { return strCurrencyUnits; }
|
||||
uint32_t BIP44CoinType() const { return bip44CoinType; }
|
||||
/** Make miner stop after a block is found. In RPC, don't return until nGenProcLimit blocks are generated */
|
||||
bool MineBlocksOnDemand() const { return fMineBlocksOnDemand; }
|
||||
/** In the future use NetworkIDString() for RPC fields */
|
||||
@@ -83,6 +92,7 @@ public:
|
||||
std::string NetworkIDString() const { return strNetworkID; }
|
||||
const std::vector<CDNSSeedData>& DNSSeeds() const { return vSeeds; }
|
||||
const std::vector<unsigned char>& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; }
|
||||
const std::string& Bech32HRP(Bech32Type type) const { return bech32HRPs[type]; }
|
||||
const std::vector<SeedSpec6>& FixedSeeds() const { return vFixedSeeds; }
|
||||
const CCheckpointData& Checkpoints() const { return checkpointData; }
|
||||
/** Return the founder's reward address and script for a given block height */
|
||||
@@ -99,7 +109,6 @@ public:
|
||||
//void settimestamp(uint32_t timestamp) { genesis.nTime = timestamp; }
|
||||
//void setgenesis(CBlock &block) { genesis = block; }
|
||||
//void recalc_genesis(uint32_t nonce) { genesis = CreateGenesisBlock(ASSETCHAINS_TIMESTAMP, nonce, GENESIS_NBITS, 1, COIN); };
|
||||
int nDefaultPort = 0;
|
||||
CMessageHeader::MessageStartChars pchMessageStart; // jl777 moved
|
||||
Consensus::Params consensus;
|
||||
|
||||
@@ -110,13 +119,16 @@ protected:
|
||||
std::vector<unsigned char> vAlertPubKey;
|
||||
int nMinerThreads = 0;
|
||||
long nMaxTipAge = 0;
|
||||
int nDefaultPort = 0;
|
||||
uint64_t nPruneAfterHeight = 0;
|
||||
unsigned int nEquihashN = 0;
|
||||
unsigned int nEquihashK = 0;
|
||||
std::vector<CDNSSeedData> vSeeds;
|
||||
std::vector<unsigned char> base58Prefixes[MAX_BASE58_TYPES];
|
||||
std::string bech32HRPs[MAX_BECH32_TYPES];
|
||||
std::string strNetworkID;
|
||||
std::string strCurrencyUnits;
|
||||
uint32_t bip44CoinType;
|
||||
CBlock genesis;
|
||||
std::vector<SeedSpec6> vFixedSeeds;
|
||||
bool fMiningRequiresPeers = false;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
//! These need to be macros, as clientversion.cpp's and bitcoin*-res.rc's voodoo requires it
|
||||
#define CLIENT_VERSION_MAJOR 1
|
||||
#define CLIENT_VERSION_MAJOR 2
|
||||
#define CLIENT_VERSION_MINOR 0
|
||||
#define CLIENT_VERSION_REVISION 15
|
||||
#define CLIENT_VERSION_BUILD 52
|
||||
|
||||
369
src/coins.cpp
369
src/coins.cpp
@@ -44,34 +44,42 @@ bool CCoins::Spend(uint32_t nPos)
|
||||
Cleanup();
|
||||
return true;
|
||||
}
|
||||
bool CCoinsView::GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const { return false; }
|
||||
bool CCoinsView::GetNullifier(const uint256 &nullifier) const { return false; }
|
||||
bool CCoinsView::GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const { return false; }
|
||||
bool CCoinsView::GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const { return false; }
|
||||
bool CCoinsView::GetNullifier(const uint256 &nullifier, ShieldedType type) const { return false; }
|
||||
bool CCoinsView::GetCoins(const uint256 &txid, CCoins &coins) const { return false; }
|
||||
bool CCoinsView::HaveCoins(const uint256 &txid) const { return false; }
|
||||
uint256 CCoinsView::GetBestBlock() const { return uint256(); }
|
||||
uint256 CCoinsView::GetBestAnchor() const { return uint256(); };
|
||||
uint256 CCoinsView::GetBestAnchor(ShieldedType type) const { return uint256(); };
|
||||
bool CCoinsView::BatchWrite(CCoinsMap &mapCoins,
|
||||
const uint256 &hashBlock,
|
||||
const uint256 &hashAnchor,
|
||||
CAnchorsMap &mapAnchors,
|
||||
CNullifiersMap &mapNullifiers) { return false; }
|
||||
const uint256 &hashSproutAnchor,
|
||||
const uint256 &hashSaplingAnchor,
|
||||
CAnchorsSproutMap &mapSproutAnchors,
|
||||
CAnchorsSaplingMap &mapSaplingAnchors,
|
||||
CNullifiersMap &mapSproutNullifiers,
|
||||
CNullifiersMap &mapSaplingNullifiers) { return false; }
|
||||
bool CCoinsView::GetStats(CCoinsStats &stats) const { return false; }
|
||||
|
||||
|
||||
CCoinsViewBacked::CCoinsViewBacked(CCoinsView *viewIn) : base(viewIn) { }
|
||||
|
||||
bool CCoinsViewBacked::GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const { return base->GetAnchorAt(rt, tree); }
|
||||
bool CCoinsViewBacked::GetNullifier(const uint256 &nullifier) const { return base->GetNullifier(nullifier); }
|
||||
bool CCoinsViewBacked::GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const { return base->GetSproutAnchorAt(rt, tree); }
|
||||
bool CCoinsViewBacked::GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const { return base->GetSaplingAnchorAt(rt, tree); }
|
||||
bool CCoinsViewBacked::GetNullifier(const uint256 &nullifier, ShieldedType type) const { return base->GetNullifier(nullifier, type); }
|
||||
bool CCoinsViewBacked::GetCoins(const uint256 &txid, CCoins &coins) const { return base->GetCoins(txid, coins); }
|
||||
bool CCoinsViewBacked::HaveCoins(const uint256 &txid) const { return base->HaveCoins(txid); }
|
||||
uint256 CCoinsViewBacked::GetBestBlock() const { return base->GetBestBlock(); }
|
||||
uint256 CCoinsViewBacked::GetBestAnchor() const { return base->GetBestAnchor(); }
|
||||
uint256 CCoinsViewBacked::GetBestAnchor(ShieldedType type) const { return base->GetBestAnchor(type); }
|
||||
void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; }
|
||||
bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins,
|
||||
const uint256 &hashBlock,
|
||||
const uint256 &hashAnchor,
|
||||
CAnchorsMap &mapAnchors,
|
||||
CNullifiersMap &mapNullifiers) { return base->BatchWrite(mapCoins, hashBlock, hashAnchor, mapAnchors, mapNullifiers); }
|
||||
const uint256 &hashSproutAnchor,
|
||||
const uint256 &hashSaplingAnchor,
|
||||
CAnchorsSproutMap &mapSproutAnchors,
|
||||
CAnchorsSaplingMap &mapSaplingAnchors,
|
||||
CNullifiersMap &mapSproutNullifiers,
|
||||
CNullifiersMap &mapSaplingNullifiers) { return base->BatchWrite(mapCoins, hashBlock, hashSproutAnchor, hashSaplingAnchor, mapSproutAnchors, mapSaplingAnchors, mapSproutNullifiers, mapSaplingNullifiers); }
|
||||
bool CCoinsViewBacked::GetStats(CCoinsStats &stats) const { return base->GetStats(stats); }
|
||||
|
||||
CCoinsKeyHasher::CCoinsKeyHasher() : salt(GetRandHash()) {}
|
||||
@@ -85,8 +93,10 @@ CCoinsViewCache::~CCoinsViewCache()
|
||||
|
||||
size_t CCoinsViewCache::DynamicMemoryUsage() const {
|
||||
return memusage::DynamicUsage(cacheCoins) +
|
||||
memusage::DynamicUsage(cacheAnchors) +
|
||||
memusage::DynamicUsage(cacheNullifiers) +
|
||||
memusage::DynamicUsage(cacheSproutAnchors) +
|
||||
memusage::DynamicUsage(cacheSaplingAnchors) +
|
||||
memusage::DynamicUsage(cacheSproutNullifiers) +
|
||||
memusage::DynamicUsage(cacheSaplingNullifiers) +
|
||||
cachedCoinsUsage;
|
||||
}
|
||||
|
||||
@@ -109,9 +119,9 @@ CCoinsMap::const_iterator CCoinsViewCache::FetchCoins(const uint256 &txid) const
|
||||
}
|
||||
|
||||
|
||||
bool CCoinsViewCache::GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const {
|
||||
CAnchorsMap::const_iterator it = cacheAnchors.find(rt);
|
||||
if (it != cacheAnchors.end()) {
|
||||
bool CCoinsViewCache::GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const {
|
||||
CAnchorsSproutMap::const_iterator it = cacheSproutAnchors.find(rt);
|
||||
if (it != cacheSproutAnchors.end()) {
|
||||
if (it->second.entered) {
|
||||
tree = it->second.tree;
|
||||
return true;
|
||||
@@ -120,11 +130,11 @@ bool CCoinsViewCache::GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tr
|
||||
}
|
||||
}
|
||||
|
||||
if (!base->GetAnchorAt(rt, tree)) {
|
||||
if (!base->GetSproutAnchorAt(rt, tree)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CAnchorsMap::iterator ret = cacheAnchors.insert(std::make_pair(rt, CAnchorsCacheEntry())).first;
|
||||
CAnchorsSproutMap::iterator ret = cacheSproutAnchors.insert(std::make_pair(rt, CAnchorsSproutCacheEntry())).first;
|
||||
ret->second.entered = true;
|
||||
ret->second.tree = tree;
|
||||
cachedCoinsUsage += ret->second.tree.DynamicMemoryUsage();
|
||||
@@ -132,24 +142,65 @@ bool CCoinsViewCache::GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tr
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CCoinsViewCache::GetNullifier(const uint256 &nullifier) const {
|
||||
CNullifiersMap::iterator it = cacheNullifiers.find(nullifier);
|
||||
if (it != cacheNullifiers.end())
|
||||
bool CCoinsViewCache::GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const {
|
||||
CAnchorsSaplingMap::const_iterator it = cacheSaplingAnchors.find(rt);
|
||||
if (it != cacheSaplingAnchors.end()) {
|
||||
if (it->second.entered) {
|
||||
tree = it->second.tree;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!base->GetSaplingAnchorAt(rt, tree)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CAnchorsSaplingMap::iterator ret = cacheSaplingAnchors.insert(std::make_pair(rt, CAnchorsSaplingCacheEntry())).first;
|
||||
ret->second.entered = true;
|
||||
ret->second.tree = tree;
|
||||
cachedCoinsUsage += ret->second.tree.DynamicMemoryUsage();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CCoinsViewCache::GetNullifier(const uint256 &nullifier, ShieldedType type) const {
|
||||
CNullifiersMap* cacheToUse;
|
||||
switch (type) {
|
||||
case SPROUT:
|
||||
cacheToUse = &cacheSproutNullifiers;
|
||||
break;
|
||||
case SAPLING:
|
||||
cacheToUse = &cacheSaplingNullifiers;
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Unknown shielded type");
|
||||
}
|
||||
CNullifiersMap::iterator it = cacheToUse->find(nullifier);
|
||||
if (it != cacheToUse->end())
|
||||
return it->second.entered;
|
||||
|
||||
CNullifiersCacheEntry entry;
|
||||
bool tmp = base->GetNullifier(nullifier);
|
||||
bool tmp = base->GetNullifier(nullifier, type);
|
||||
entry.entered = tmp;
|
||||
|
||||
cacheNullifiers.insert(std::make_pair(nullifier, entry));
|
||||
cacheToUse->insert(std::make_pair(nullifier, entry));
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
void CCoinsViewCache::PushAnchor(const ZCIncrementalMerkleTree &tree) {
|
||||
template<typename Tree, typename Cache, typename CacheIterator, typename CacheEntry>
|
||||
void CCoinsViewCache::AbstractPushAnchor(
|
||||
const Tree &tree,
|
||||
ShieldedType type,
|
||||
Cache &cacheAnchors,
|
||||
uint256 &hash
|
||||
)
|
||||
{
|
||||
uint256 newrt = tree.root();
|
||||
|
||||
auto currentRoot = GetBestAnchor();
|
||||
auto currentRoot = GetBestAnchor(type);
|
||||
|
||||
// We don't want to overwrite an anchor we already have.
|
||||
// This occurs when a block doesn't modify mapAnchors at all,
|
||||
@@ -157,24 +208,69 @@ void CCoinsViewCache::PushAnchor(const ZCIncrementalMerkleTree &tree) {
|
||||
// different way (make all blocks modify mapAnchors somehow)
|
||||
// but this is simpler to reason about.
|
||||
if (currentRoot != newrt) {
|
||||
auto insertRet = cacheAnchors.insert(std::make_pair(newrt, CAnchorsCacheEntry()));
|
||||
CAnchorsMap::iterator ret = insertRet.first;
|
||||
auto insertRet = cacheAnchors.insert(std::make_pair(newrt, CacheEntry()));
|
||||
CacheIterator ret = insertRet.first;
|
||||
|
||||
ret->second.entered = true;
|
||||
ret->second.tree = tree;
|
||||
ret->second.flags = CAnchorsCacheEntry::DIRTY;
|
||||
ret->second.flags = CacheEntry::DIRTY;
|
||||
|
||||
if (insertRet.second) {
|
||||
// An insert took place
|
||||
cachedCoinsUsage += ret->second.tree.DynamicMemoryUsage();
|
||||
}
|
||||
|
||||
hashAnchor = newrt;
|
||||
hash = newrt;
|
||||
}
|
||||
}
|
||||
|
||||
void CCoinsViewCache::PopAnchor(const uint256 &newrt) {
|
||||
auto currentRoot = GetBestAnchor();
|
||||
template<> void CCoinsViewCache::PushAnchor(const SproutMerkleTree &tree)
|
||||
{
|
||||
AbstractPushAnchor<SproutMerkleTree, CAnchorsSproutMap, CAnchorsSproutMap::iterator, CAnchorsSproutCacheEntry>(
|
||||
tree,
|
||||
SPROUT,
|
||||
cacheSproutAnchors,
|
||||
hashSproutAnchor
|
||||
);
|
||||
}
|
||||
|
||||
template<> void CCoinsViewCache::PushAnchor(const SaplingMerkleTree &tree)
|
||||
{
|
||||
AbstractPushAnchor<SaplingMerkleTree, CAnchorsSaplingMap, CAnchorsSaplingMap::iterator, CAnchorsSaplingCacheEntry>(
|
||||
tree,
|
||||
SAPLING,
|
||||
cacheSaplingAnchors,
|
||||
hashSaplingAnchor
|
||||
);
|
||||
}
|
||||
|
||||
template<>
|
||||
void CCoinsViewCache::BringBestAnchorIntoCache(
|
||||
const uint256 ¤tRoot,
|
||||
SproutMerkleTree &tree
|
||||
)
|
||||
{
|
||||
assert(GetSproutAnchorAt(currentRoot, tree));
|
||||
}
|
||||
|
||||
template<>
|
||||
void CCoinsViewCache::BringBestAnchorIntoCache(
|
||||
const uint256 ¤tRoot,
|
||||
SaplingMerkleTree &tree
|
||||
)
|
||||
{
|
||||
assert(GetSaplingAnchorAt(currentRoot, tree));
|
||||
}
|
||||
|
||||
template<typename Tree, typename Cache, typename CacheEntry>
|
||||
void CCoinsViewCache::AbstractPopAnchor(
|
||||
const uint256 &newrt,
|
||||
ShieldedType type,
|
||||
Cache &cacheAnchors,
|
||||
uint256 &hash
|
||||
)
|
||||
{
|
||||
auto currentRoot = GetBestAnchor(type);
|
||||
|
||||
// Blocks might not change the commitment tree, in which
|
||||
// case restoring the "old" anchor during a reorg must
|
||||
@@ -183,25 +279,57 @@ void CCoinsViewCache::PopAnchor(const uint256 &newrt) {
|
||||
// Bring the current best anchor into our local cache
|
||||
// so that its tree exists in memory.
|
||||
{
|
||||
ZCIncrementalMerkleTree tree;
|
||||
assert(GetAnchorAt(currentRoot, tree));
|
||||
Tree tree;
|
||||
BringBestAnchorIntoCache(currentRoot, tree);
|
||||
}
|
||||
|
||||
// Mark the anchor as unentered, removing it from view
|
||||
cacheAnchors[currentRoot].entered = false;
|
||||
|
||||
// Mark the cache entry as dirty so it's propagated
|
||||
cacheAnchors[currentRoot].flags = CAnchorsCacheEntry::DIRTY;
|
||||
cacheAnchors[currentRoot].flags = CacheEntry::DIRTY;
|
||||
|
||||
// Mark the new root as the best anchor
|
||||
hashAnchor = newrt;
|
||||
hash = newrt;
|
||||
}
|
||||
}
|
||||
|
||||
void CCoinsViewCache::SetNullifier(const uint256 &nullifier, bool spent) {
|
||||
std::pair<CNullifiersMap::iterator, bool> ret = cacheNullifiers.insert(std::make_pair(nullifier, CNullifiersCacheEntry()));
|
||||
ret.first->second.entered = spent;
|
||||
ret.first->second.flags |= CNullifiersCacheEntry::DIRTY;
|
||||
void CCoinsViewCache::PopAnchor(const uint256 &newrt, ShieldedType type) {
|
||||
switch (type) {
|
||||
case SPROUT:
|
||||
AbstractPopAnchor<SproutMerkleTree, CAnchorsSproutMap, CAnchorsSproutCacheEntry>(
|
||||
newrt,
|
||||
SPROUT,
|
||||
cacheSproutAnchors,
|
||||
hashSproutAnchor
|
||||
);
|
||||
break;
|
||||
case SAPLING:
|
||||
AbstractPopAnchor<SaplingMerkleTree, CAnchorsSaplingMap, CAnchorsSaplingCacheEntry>(
|
||||
newrt,
|
||||
SAPLING,
|
||||
cacheSaplingAnchors,
|
||||
hashSaplingAnchor
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Unknown shielded type");
|
||||
}
|
||||
}
|
||||
|
||||
void CCoinsViewCache::SetNullifiers(const CTransaction& tx, bool spent) {
|
||||
for (const JSDescription &joinsplit : tx.vjoinsplit) {
|
||||
for (const uint256 &nullifier : joinsplit.nullifiers) {
|
||||
std::pair<CNullifiersMap::iterator, bool> ret = cacheSproutNullifiers.insert(std::make_pair(nullifier, CNullifiersCacheEntry()));
|
||||
ret.first->second.entered = spent;
|
||||
ret.first->second.flags |= CNullifiersCacheEntry::DIRTY;
|
||||
}
|
||||
}
|
||||
for (const SpendDescription &spendDescription : tx.vShieldedSpend) {
|
||||
std::pair<CNullifiersMap::iterator, bool> ret = cacheSaplingNullifiers.insert(std::make_pair(spendDescription.nullifier, CNullifiersCacheEntry()));
|
||||
ret.first->second.entered = spent;
|
||||
ret.first->second.flags |= CNullifiersCacheEntry::DIRTY;
|
||||
}
|
||||
}
|
||||
|
||||
bool CCoinsViewCache::GetCoins(const uint256 &txid, CCoins &coins) const {
|
||||
@@ -268,21 +396,90 @@ uint256 CCoinsViewCache::GetBestBlock() const {
|
||||
}
|
||||
|
||||
|
||||
uint256 CCoinsViewCache::GetBestAnchor() const {
|
||||
if (hashAnchor.IsNull())
|
||||
hashAnchor = base->GetBestAnchor();
|
||||
return hashAnchor;
|
||||
uint256 CCoinsViewCache::GetBestAnchor(ShieldedType type) const {
|
||||
switch (type) {
|
||||
case SPROUT:
|
||||
if (hashSproutAnchor.IsNull())
|
||||
hashSproutAnchor = base->GetBestAnchor(type);
|
||||
return hashSproutAnchor;
|
||||
break;
|
||||
case SAPLING:
|
||||
if (hashSaplingAnchor.IsNull())
|
||||
hashSaplingAnchor = base->GetBestAnchor(type);
|
||||
return hashSaplingAnchor;
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Unknown shielded type");
|
||||
}
|
||||
}
|
||||
|
||||
void CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) {
|
||||
hashBlock = hashBlockIn;
|
||||
}
|
||||
|
||||
void BatchWriteNullifiers(CNullifiersMap &mapNullifiers, CNullifiersMap &cacheNullifiers)
|
||||
{
|
||||
for (CNullifiersMap::iterator child_it = mapNullifiers.begin(); child_it != mapNullifiers.end();) {
|
||||
if (child_it->second.flags & CNullifiersCacheEntry::DIRTY) { // Ignore non-dirty entries (optimization).
|
||||
CNullifiersMap::iterator parent_it = cacheNullifiers.find(child_it->first);
|
||||
|
||||
if (parent_it == cacheNullifiers.end()) {
|
||||
CNullifiersCacheEntry& entry = cacheNullifiers[child_it->first];
|
||||
entry.entered = child_it->second.entered;
|
||||
entry.flags = CNullifiersCacheEntry::DIRTY;
|
||||
} else {
|
||||
if (parent_it->second.entered != child_it->second.entered) {
|
||||
parent_it->second.entered = child_it->second.entered;
|
||||
parent_it->second.flags |= CNullifiersCacheEntry::DIRTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
CNullifiersMap::iterator itOld = child_it++;
|
||||
mapNullifiers.erase(itOld);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Map, typename MapIterator, typename MapEntry>
|
||||
void BatchWriteAnchors(
|
||||
Map &mapAnchors,
|
||||
Map &cacheAnchors,
|
||||
size_t &cachedCoinsUsage
|
||||
)
|
||||
{
|
||||
for (MapIterator child_it = mapAnchors.begin(); child_it != mapAnchors.end();)
|
||||
{
|
||||
if (child_it->second.flags & MapEntry::DIRTY) {
|
||||
MapIterator parent_it = cacheAnchors.find(child_it->first);
|
||||
|
||||
if (parent_it == cacheAnchors.end()) {
|
||||
MapEntry& entry = cacheAnchors[child_it->first];
|
||||
entry.entered = child_it->second.entered;
|
||||
entry.tree = child_it->second.tree;
|
||||
entry.flags = MapEntry::DIRTY;
|
||||
|
||||
cachedCoinsUsage += entry.tree.DynamicMemoryUsage();
|
||||
} else {
|
||||
if (parent_it->second.entered != child_it->second.entered) {
|
||||
// The parent may have removed the entry.
|
||||
parent_it->second.entered = child_it->second.entered;
|
||||
parent_it->second.flags |= MapEntry::DIRTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MapIterator itOld = child_it++;
|
||||
mapAnchors.erase(itOld);
|
||||
}
|
||||
}
|
||||
|
||||
bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins,
|
||||
const uint256 &hashBlockIn,
|
||||
const uint256 &hashAnchorIn,
|
||||
CAnchorsMap &mapAnchors,
|
||||
CNullifiersMap &mapNullifiers) {
|
||||
const uint256 &hashSproutAnchorIn,
|
||||
const uint256 &hashSaplingAnchorIn,
|
||||
CAnchorsSproutMap &mapSproutAnchors,
|
||||
CAnchorsSaplingMap &mapSaplingAnchors,
|
||||
CNullifiersMap &mapSproutNullifiers,
|
||||
CNullifiersMap &mapSaplingNullifiers) {
|
||||
assert(!hasModifier);
|
||||
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) {
|
||||
if (it->second.flags & CCoinsCacheEntry::DIRTY) { // Ignore non-dirty entries (optimization).
|
||||
@@ -319,61 +516,25 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins,
|
||||
mapCoins.erase(itOld);
|
||||
}
|
||||
|
||||
for (CAnchorsMap::iterator child_it = mapAnchors.begin(); child_it != mapAnchors.end();)
|
||||
{
|
||||
if (child_it->second.flags & CAnchorsCacheEntry::DIRTY) {
|
||||
CAnchorsMap::iterator parent_it = cacheAnchors.find(child_it->first);
|
||||
::BatchWriteAnchors<CAnchorsSproutMap, CAnchorsSproutMap::iterator, CAnchorsSproutCacheEntry>(mapSproutAnchors, cacheSproutAnchors, cachedCoinsUsage);
|
||||
::BatchWriteAnchors<CAnchorsSaplingMap, CAnchorsSaplingMap::iterator, CAnchorsSaplingCacheEntry>(mapSaplingAnchors, cacheSaplingAnchors, cachedCoinsUsage);
|
||||
|
||||
if (parent_it == cacheAnchors.end()) {
|
||||
CAnchorsCacheEntry& entry = cacheAnchors[child_it->first];
|
||||
entry.entered = child_it->second.entered;
|
||||
entry.tree = child_it->second.tree;
|
||||
entry.flags = CAnchorsCacheEntry::DIRTY;
|
||||
::BatchWriteNullifiers(mapSproutNullifiers, cacheSproutNullifiers);
|
||||
::BatchWriteNullifiers(mapSaplingNullifiers, cacheSaplingNullifiers);
|
||||
|
||||
cachedCoinsUsage += entry.tree.DynamicMemoryUsage();
|
||||
} else {
|
||||
if (parent_it->second.entered != child_it->second.entered) {
|
||||
// The parent may have removed the entry.
|
||||
parent_it->second.entered = child_it->second.entered;
|
||||
parent_it->second.flags |= CAnchorsCacheEntry::DIRTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CAnchorsMap::iterator itOld = child_it++;
|
||||
mapAnchors.erase(itOld);
|
||||
}
|
||||
|
||||
for (CNullifiersMap::iterator child_it = mapNullifiers.begin(); child_it != mapNullifiers.end();)
|
||||
{
|
||||
if (child_it->second.flags & CNullifiersCacheEntry::DIRTY) { // Ignore non-dirty entries (optimization).
|
||||
CNullifiersMap::iterator parent_it = cacheNullifiers.find(child_it->first);
|
||||
|
||||
if (parent_it == cacheNullifiers.end()) {
|
||||
CNullifiersCacheEntry& entry = cacheNullifiers[child_it->first];
|
||||
entry.entered = child_it->second.entered;
|
||||
entry.flags = CNullifiersCacheEntry::DIRTY;
|
||||
} else {
|
||||
if (parent_it->second.entered != child_it->second.entered) {
|
||||
parent_it->second.entered = child_it->second.entered;
|
||||
parent_it->second.flags |= CNullifiersCacheEntry::DIRTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
CNullifiersMap::iterator itOld = child_it++;
|
||||
mapNullifiers.erase(itOld);
|
||||
}
|
||||
|
||||
hashAnchor = hashAnchorIn;
|
||||
hashSproutAnchor = hashSproutAnchorIn;
|
||||
hashSaplingAnchor = hashSaplingAnchorIn;
|
||||
hashBlock = hashBlockIn;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CCoinsViewCache::Flush() {
|
||||
bool fOk = base->BatchWrite(cacheCoins, hashBlock, hashAnchor, cacheAnchors, cacheNullifiers);
|
||||
bool fOk = base->BatchWrite(cacheCoins, hashBlock, hashSproutAnchor, hashSaplingAnchor, cacheSproutAnchors, cacheSaplingAnchors, cacheSproutNullifiers, cacheSaplingNullifiers);
|
||||
cacheCoins.clear();
|
||||
cacheAnchors.clear();
|
||||
cacheNullifiers.clear();
|
||||
cacheSproutAnchors.clear();
|
||||
cacheSaplingAnchors.clear();
|
||||
cacheSproutNullifiers.clear();
|
||||
cacheSaplingNullifiers.clear();
|
||||
cachedCoinsUsage = 0;
|
||||
return fOk;
|
||||
}
|
||||
@@ -445,7 +606,7 @@ CAmount CCoinsViewCache::GetValueIn(int32_t nHeight,int64_t *interestp,const CTr
|
||||
}
|
||||
#endif
|
||||
}
|
||||
nResult += tx.GetJoinSplitValueIn();
|
||||
nResult += tx.GetShieldedValueIn();
|
||||
|
||||
return nResult;
|
||||
}
|
||||
@@ -453,24 +614,24 @@ CAmount CCoinsViewCache::GetValueIn(int32_t nHeight,int64_t *interestp,const CTr
|
||||
|
||||
bool CCoinsViewCache::HaveJoinSplitRequirements(const CTransaction& tx) const
|
||||
{
|
||||
boost::unordered_map<uint256, ZCIncrementalMerkleTree, CCoinsKeyHasher> intermediates;
|
||||
boost::unordered_map<uint256, SproutMerkleTree, CCoinsKeyHasher> intermediates;
|
||||
|
||||
BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit)
|
||||
{
|
||||
BOOST_FOREACH(const uint256& nullifier, joinsplit.nullifiers)
|
||||
{
|
||||
if (GetNullifier(nullifier)) {
|
||||
if (GetNullifier(nullifier, SPROUT)) {
|
||||
// If the nullifier is set, this transaction
|
||||
// double-spends!
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ZCIncrementalMerkleTree tree;
|
||||
SproutMerkleTree tree;
|
||||
auto it = intermediates.find(joinsplit.anchor);
|
||||
if (it != intermediates.end()) {
|
||||
tree = it->second;
|
||||
} else if (!GetAnchorAt(joinsplit.anchor, tree)) {
|
||||
} else if (!GetSproutAnchorAt(joinsplit.anchor, tree)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -482,6 +643,16 @@ bool CCoinsViewCache::HaveJoinSplitRequirements(const CTransaction& tx) const
|
||||
intermediates.insert(std::make_pair(tree.root(), tree));
|
||||
}
|
||||
|
||||
for (const SpendDescription &spendDescription : tx.vShieldedSpend) {
|
||||
if (GetNullifier(spendDescription.nullifier, SAPLING)) // Prevent double spends
|
||||
return false;
|
||||
|
||||
SaplingMerkleTree tree;
|
||||
if (!GetSaplingAnchorAt(spendDescription.anchor, tree)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
175
src/coins.h
175
src/coins.h
@@ -162,31 +162,8 @@ public:
|
||||
return fCoinBase;
|
||||
}
|
||||
|
||||
unsigned int GetSerializeSize(int nType, int nVersion) const {
|
||||
unsigned int nSize = 0;
|
||||
unsigned int nMaskSize = 0, nMaskCode = 0;
|
||||
CalcMaskSize(nMaskSize, nMaskCode);
|
||||
bool fFirst = vout.size() > 0 && !vout[0].IsNull();
|
||||
bool fSecond = vout.size() > 1 && !vout[1].IsNull();
|
||||
assert(fFirst || fSecond || nMaskCode);
|
||||
unsigned int nCode = 8*(nMaskCode - (fFirst || fSecond ? 0 : 1)) + (fCoinBase ? 1 : 0) + (fFirst ? 2 : 0) + (fSecond ? 4 : 0);
|
||||
// version
|
||||
nSize += ::GetSerializeSize(VARINT(this->nVersion), nType, nVersion);
|
||||
// size of header code
|
||||
nSize += ::GetSerializeSize(VARINT(nCode), nType, nVersion);
|
||||
// spentness bitmask
|
||||
nSize += nMaskSize;
|
||||
// txouts
|
||||
for (unsigned int i = 0; i < vout.size(); i++)
|
||||
if (!vout[i].IsNull())
|
||||
nSize += ::GetSerializeSize(CTxOutCompressor(REF(vout[i])), nType, nVersion);
|
||||
// height
|
||||
nSize += ::GetSerializeSize(VARINT(nHeight), nType, nVersion);
|
||||
return nSize;
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
void Serialize(Stream &s, int nType, int nVersion) const {
|
||||
void Serialize(Stream &s) const {
|
||||
unsigned int nMaskSize = 0, nMaskCode = 0;
|
||||
CalcMaskSize(nMaskSize, nMaskCode);
|
||||
bool fFirst = vout.size() > 0 && !vout[0].IsNull();
|
||||
@@ -194,33 +171,33 @@ public:
|
||||
assert(fFirst || fSecond || nMaskCode);
|
||||
unsigned int nCode = 8*(nMaskCode - (fFirst || fSecond ? 0 : 1)) + (fCoinBase ? 1 : 0) + (fFirst ? 2 : 0) + (fSecond ? 4 : 0);
|
||||
// version
|
||||
::Serialize(s, VARINT(this->nVersion), nType, nVersion);
|
||||
::Serialize(s, VARINT(this->nVersion));
|
||||
// header code
|
||||
::Serialize(s, VARINT(nCode), nType, nVersion);
|
||||
::Serialize(s, VARINT(nCode));
|
||||
// spentness bitmask
|
||||
for (unsigned int b = 0; b<nMaskSize; b++) {
|
||||
unsigned char chAvail = 0;
|
||||
for (unsigned int i = 0; i < 8 && 2+b*8+i < vout.size(); i++)
|
||||
if (!vout[2+b*8+i].IsNull())
|
||||
chAvail |= (1 << i);
|
||||
::Serialize(s, chAvail, nType, nVersion);
|
||||
::Serialize(s, chAvail);
|
||||
}
|
||||
// txouts themself
|
||||
for (unsigned int i = 0; i < vout.size(); i++) {
|
||||
if (!vout[i].IsNull())
|
||||
::Serialize(s, CTxOutCompressor(REF(vout[i])), nType, nVersion);
|
||||
::Serialize(s, CTxOutCompressor(REF(vout[i])));
|
||||
}
|
||||
// coinbase height
|
||||
::Serialize(s, VARINT(nHeight), nType, nVersion);
|
||||
::Serialize(s, VARINT(nHeight));
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
void Unserialize(Stream &s, int nType, int nVersion) {
|
||||
void Unserialize(Stream &s) {
|
||||
unsigned int nCode = 0;
|
||||
// version
|
||||
::Unserialize(s, VARINT(this->nVersion), nType, nVersion);
|
||||
::Unserialize(s, VARINT(this->nVersion));
|
||||
// header code
|
||||
::Unserialize(s, VARINT(nCode), nType, nVersion);
|
||||
::Unserialize(s, VARINT(nCode));
|
||||
fCoinBase = nCode & 1;
|
||||
std::vector<bool> vAvail(2, false);
|
||||
vAvail[0] = (nCode & 2) != 0;
|
||||
@@ -229,7 +206,7 @@ public:
|
||||
// spentness bitmask
|
||||
while (nMaskCode > 0) {
|
||||
unsigned char chAvail = 0;
|
||||
::Unserialize(s, chAvail, nType, nVersion);
|
||||
::Unserialize(s, chAvail);
|
||||
for (unsigned int p = 0; p < 8; p++) {
|
||||
bool f = (chAvail & (1 << p)) != 0;
|
||||
vAvail.push_back(f);
|
||||
@@ -241,10 +218,10 @@ public:
|
||||
vout.assign(vAvail.size(), CTxOut());
|
||||
for (unsigned int i = 0; i < vAvail.size(); i++) {
|
||||
if (vAvail[i])
|
||||
::Unserialize(s, REF(CTxOutCompressor(vout[i])), nType, nVersion);
|
||||
::Unserialize(s, REF(CTxOutCompressor(vout[i])));
|
||||
}
|
||||
// coinbase height
|
||||
::Unserialize(s, VARINT(nHeight), nType, nVersion);
|
||||
::Unserialize(s, VARINT(nHeight));
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
@@ -313,17 +290,30 @@ struct CCoinsCacheEntry
|
||||
CCoinsCacheEntry() : coins(), flags(0) {}
|
||||
};
|
||||
|
||||
struct CAnchorsCacheEntry
|
||||
struct CAnchorsSproutCacheEntry
|
||||
{
|
||||
bool entered; // This will be false if the anchor is removed from the cache
|
||||
ZCIncrementalMerkleTree tree; // The tree itself
|
||||
SproutMerkleTree tree; // The tree itself
|
||||
unsigned char flags;
|
||||
|
||||
enum Flags {
|
||||
DIRTY = (1 << 0), // This cache entry is potentially different from the version in the parent view.
|
||||
};
|
||||
|
||||
CAnchorsCacheEntry() : entered(false), flags(0) {}
|
||||
CAnchorsSproutCacheEntry() : entered(false), flags(0) {}
|
||||
};
|
||||
|
||||
struct CAnchorsSaplingCacheEntry
|
||||
{
|
||||
bool entered; // This will be false if the anchor is removed from the cache
|
||||
SaplingMerkleTree tree; // The tree itself
|
||||
unsigned char flags;
|
||||
|
||||
enum Flags {
|
||||
DIRTY = (1 << 0), // This cache entry is potentially different from the version in the parent view.
|
||||
};
|
||||
|
||||
CAnchorsSaplingCacheEntry() : entered(false), flags(0) {}
|
||||
};
|
||||
|
||||
struct CNullifiersCacheEntry
|
||||
@@ -338,8 +328,15 @@ struct CNullifiersCacheEntry
|
||||
CNullifiersCacheEntry() : entered(false), flags(0) {}
|
||||
};
|
||||
|
||||
enum ShieldedType
|
||||
{
|
||||
SPROUT,
|
||||
SAPLING,
|
||||
};
|
||||
|
||||
typedef boost::unordered_map<uint256, CCoinsCacheEntry, CCoinsKeyHasher> CCoinsMap;
|
||||
typedef boost::unordered_map<uint256, CAnchorsCacheEntry, CCoinsKeyHasher> CAnchorsMap;
|
||||
typedef boost::unordered_map<uint256, CAnchorsSproutCacheEntry, CCoinsKeyHasher> CAnchorsSproutMap;
|
||||
typedef boost::unordered_map<uint256, CAnchorsSaplingCacheEntry, CCoinsKeyHasher> CAnchorsSaplingMap;
|
||||
typedef boost::unordered_map<uint256, CNullifiersCacheEntry, CCoinsKeyHasher> CNullifiersMap;
|
||||
|
||||
struct CCoinsStats
|
||||
@@ -360,11 +357,14 @@ struct CCoinsStats
|
||||
class CCoinsView
|
||||
{
|
||||
public:
|
||||
//! Retrieve the tree at a particular anchored root in the chain
|
||||
virtual bool GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const;
|
||||
//! Retrieve the tree (Sprout) at a particular anchored root in the chain
|
||||
virtual bool GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const;
|
||||
|
||||
//! Retrieve the tree (Sapling) at a particular anchored root in the chain
|
||||
virtual bool GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const;
|
||||
|
||||
//! Determine whether a nullifier is spent or not
|
||||
virtual bool GetNullifier(const uint256 &nullifier) const;
|
||||
virtual bool GetNullifier(const uint256 &nullifier, ShieldedType type) const;
|
||||
|
||||
//! Retrieve the CCoins (unspent transaction outputs) for a given txid
|
||||
virtual bool GetCoins(const uint256 &txid, CCoins &coins) const;
|
||||
@@ -377,15 +377,18 @@ public:
|
||||
virtual uint256 GetBestBlock() const;
|
||||
|
||||
//! Get the current "tip" or the latest anchored tree root in the chain
|
||||
virtual uint256 GetBestAnchor() const;
|
||||
virtual uint256 GetBestAnchor(ShieldedType type) const;
|
||||
|
||||
//! Do a bulk modification (multiple CCoins changes + BestBlock change).
|
||||
//! The passed mapCoins can be modified.
|
||||
virtual bool BatchWrite(CCoinsMap &mapCoins,
|
||||
const uint256 &hashBlock,
|
||||
const uint256 &hashAnchor,
|
||||
CAnchorsMap &mapAnchors,
|
||||
CNullifiersMap &mapNullifiers);
|
||||
const uint256 &hashSproutAnchor,
|
||||
const uint256 &hashSaplingAnchor,
|
||||
CAnchorsSproutMap &mapSproutAnchors,
|
||||
CAnchorsSaplingMap &mapSaplingAnchors,
|
||||
CNullifiersMap &mapSproutNullifiers,
|
||||
CNullifiersMap &mapSaplingNullifiers);
|
||||
|
||||
//! Calculate statistics about the unspent transaction output set
|
||||
virtual bool GetStats(CCoinsStats &stats) const;
|
||||
@@ -403,18 +406,22 @@ protected:
|
||||
|
||||
public:
|
||||
CCoinsViewBacked(CCoinsView *viewIn);
|
||||
bool GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const;
|
||||
bool GetNullifier(const uint256 &nullifier) const;
|
||||
bool GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const;
|
||||
bool GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const;
|
||||
bool GetNullifier(const uint256 &nullifier, ShieldedType type) const;
|
||||
bool GetCoins(const uint256 &txid, CCoins &coins) const;
|
||||
bool HaveCoins(const uint256 &txid) const;
|
||||
uint256 GetBestBlock() const;
|
||||
uint256 GetBestAnchor() const;
|
||||
uint256 GetBestAnchor(ShieldedType type) const;
|
||||
void SetBackend(CCoinsView &viewIn);
|
||||
bool BatchWrite(CCoinsMap &mapCoins,
|
||||
const uint256 &hashBlock,
|
||||
const uint256 &hashAnchor,
|
||||
CAnchorsMap &mapAnchors,
|
||||
CNullifiersMap &mapNullifiers);
|
||||
const uint256 &hashSproutAnchor,
|
||||
const uint256 &hashSaplingAnchor,
|
||||
CAnchorsSproutMap &mapSproutAnchors,
|
||||
CAnchorsSaplingMap &mapSaplingAnchors,
|
||||
CNullifiersMap &mapSproutNullifiers,
|
||||
CNullifiersMap &mapSaplingNullifiers);
|
||||
bool GetStats(CCoinsStats &stats) const;
|
||||
};
|
||||
|
||||
@@ -485,9 +492,12 @@ protected:
|
||||
*/
|
||||
mutable uint256 hashBlock;
|
||||
mutable CCoinsMap cacheCoins;
|
||||
mutable uint256 hashAnchor;
|
||||
mutable CAnchorsMap cacheAnchors;
|
||||
mutable CNullifiersMap cacheNullifiers;
|
||||
mutable uint256 hashSproutAnchor;
|
||||
mutable uint256 hashSaplingAnchor;
|
||||
mutable CAnchorsSproutMap cacheSproutAnchors;
|
||||
mutable CAnchorsSaplingMap cacheSaplingAnchors;
|
||||
mutable CNullifiersMap cacheSproutNullifiers;
|
||||
mutable CNullifiersMap cacheSaplingNullifiers;
|
||||
|
||||
/* Cached dynamic memory usage for the inner CCoins objects. */
|
||||
mutable size_t cachedCoinsUsage;
|
||||
@@ -498,30 +508,34 @@ public:
|
||||
|
||||
// Standard CCoinsView methods
|
||||
static CLaunchMap &LaunchMap() { return launchMap; }
|
||||
bool GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const;
|
||||
bool GetNullifier(const uint256 &nullifier) const;
|
||||
bool GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const;
|
||||
bool GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const;
|
||||
bool GetNullifier(const uint256 &nullifier, ShieldedType type) const;
|
||||
bool GetCoins(const uint256 &txid, CCoins &coins) const;
|
||||
bool HaveCoins(const uint256 &txid) const;
|
||||
uint256 GetBestBlock() const;
|
||||
uint256 GetBestAnchor() const;
|
||||
uint256 GetBestAnchor(ShieldedType type) const;
|
||||
void SetBestBlock(const uint256 &hashBlock);
|
||||
bool BatchWrite(CCoinsMap &mapCoins,
|
||||
const uint256 &hashBlock,
|
||||
const uint256 &hashAnchor,
|
||||
CAnchorsMap &mapAnchors,
|
||||
CNullifiersMap &mapNullifiers);
|
||||
const uint256 &hashSproutAnchor,
|
||||
const uint256 &hashSaplingAnchor,
|
||||
CAnchorsSproutMap &mapSproutAnchors,
|
||||
CAnchorsSaplingMap &mapSaplingAnchors,
|
||||
CNullifiersMap &mapSproutNullifiers,
|
||||
CNullifiersMap &mapSaplingNullifiers);
|
||||
|
||||
|
||||
// Adds the tree to mapAnchors and sets the current commitment
|
||||
// root to this root.
|
||||
void PushAnchor(const ZCIncrementalMerkleTree &tree);
|
||||
// Adds the tree to mapSproutAnchors (or mapSaplingAnchors based on the type of tree)
|
||||
// and sets the current commitment root to this root.
|
||||
template<typename Tree> void PushAnchor(const Tree &tree);
|
||||
|
||||
// Removes the current commitment root from mapAnchors and sets
|
||||
// the new current root.
|
||||
void PopAnchor(const uint256 &rt);
|
||||
void PopAnchor(const uint256 &rt, ShieldedType type);
|
||||
|
||||
// Marks a nullifier as spent or not.
|
||||
void SetNullifier(const uint256 &nullifier, bool spent);
|
||||
// Marks nullifiers for a given transaction as spent or not.
|
||||
void SetNullifiers(const CTransaction& tx, bool spent);
|
||||
|
||||
/**
|
||||
* Return a pointer to CCoins in the cache, or NULL if not found. This is
|
||||
@@ -556,7 +570,7 @@ public:
|
||||
* so may not be able to calculate this.
|
||||
*
|
||||
* @param[in] tx transaction for which we are checking input total
|
||||
* @return Sum of value of all inputs (scriptSigs)
|
||||
* @return Sum of value of all inputs (scriptSigs), (positive valueBalance or zero) and JoinSplit vpub_new
|
||||
*/
|
||||
CAmount GetValueIn(int32_t nHeight,int64_t *interestp,const CTransaction& tx,uint32_t prevblocktime) const;
|
||||
|
||||
@@ -583,6 +597,31 @@ private:
|
||||
* By making the copy constructor private, we prevent accidentally using it when one intends to create a cache on top of a base cache.
|
||||
*/
|
||||
CCoinsViewCache(const CCoinsViewCache &);
|
||||
|
||||
//! Generalized interface for popping anchors
|
||||
template<typename Tree, typename Cache, typename CacheEntry>
|
||||
void AbstractPopAnchor(
|
||||
const uint256 &newrt,
|
||||
ShieldedType type,
|
||||
Cache &cacheAnchors,
|
||||
uint256 &hash
|
||||
);
|
||||
|
||||
//! Generalized interface for pushing anchors
|
||||
template<typename Tree, typename Cache, typename CacheIterator, typename CacheEntry>
|
||||
void AbstractPushAnchor(
|
||||
const Tree &tree,
|
||||
ShieldedType type,
|
||||
Cache &cacheAnchors,
|
||||
uint256 &hash
|
||||
);
|
||||
|
||||
//! Interface for bringing an anchor into the cache.
|
||||
template<typename Tree>
|
||||
void BringBestAnchorIntoCache(
|
||||
const uint256 ¤tRoot,
|
||||
Tree &tree
|
||||
);
|
||||
};
|
||||
|
||||
#endif // BITCOIN_COINS_H
|
||||
|
||||
@@ -55,16 +55,8 @@ protected:
|
||||
public:
|
||||
CScriptCompressor(CScript &scriptIn) : script(scriptIn) { }
|
||||
|
||||
unsigned int GetSerializeSize(int nType, int nVersion) const {
|
||||
std::vector<unsigned char> compr;
|
||||
if (Compress(compr))
|
||||
return compr.size();
|
||||
unsigned int nSize = script.size() + nSpecialScripts;
|
||||
return script.size() + VARINT(nSize).GetSerializeSize(nType, nVersion);
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
void Serialize(Stream &s, int nType, int nVersion) const {
|
||||
void Serialize(Stream &s) const {
|
||||
std::vector<unsigned char> compr;
|
||||
if (Compress(compr)) {
|
||||
s << CFlatData(compr);
|
||||
@@ -76,7 +68,7 @@ public:
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
void Unserialize(Stream &s, int nType, int nVersion) {
|
||||
void Unserialize(Stream &s) {
|
||||
unsigned int nSize = 0;
|
||||
s >> VARINT(nSize);
|
||||
if (nSize < nSpecialScripts) {
|
||||
@@ -112,7 +104,7 @@ public:
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
if (!ser_action.ForRead()) {
|
||||
uint64_t nVal = CompressAmount(txout.nValue);
|
||||
READWRITE(VARINT(nVal));
|
||||
|
||||
@@ -10,16 +10,21 @@
|
||||
static const int32_t MIN_BLOCK_VERSION = 4;
|
||||
/** The minimum allowed transaction version (network rule) */
|
||||
static const int32_t SPROUT_MIN_TX_VERSION = 1;
|
||||
/** The minimum allowed transaction version (network rule) */
|
||||
/** The minimum allowed Overwinter transaction version (network rule) */
|
||||
static const int32_t OVERWINTER_MIN_TX_VERSION = 3;
|
||||
/** The maximum allowed transaction version (network rule) */
|
||||
/** The maximum allowed Overwinter transaction version (network rule) */
|
||||
static const int32_t OVERWINTER_MAX_TX_VERSION = 3;
|
||||
/** The minimum allowed Sapling transaction version (network rule) */
|
||||
static const int32_t SAPLING_MIN_TX_VERSION = 4;
|
||||
/** The maximum allowed Sapling transaction version (network rule) */
|
||||
static const int32_t SAPLING_MAX_TX_VERSION = 4;
|
||||
/** The maximum allowed size for a serialized block, in bytes (network rule) */
|
||||
static const unsigned int MAX_BLOCK_SIZE = 2000000;
|
||||
/** The maximum allowed number of signature check operations in a block (network rule) */
|
||||
extern unsigned int MAX_BLOCK_SIGOPS;
|
||||
/** The maximum size of a transaction (network rule) */
|
||||
static const unsigned int MAX_TX_SIZE = 100000;
|
||||
static const unsigned int MAX_TX_SIZE_BEFORE_SAPLING = 100000;
|
||||
static const unsigned int MAX_TX_SIZE_AFTER_SAPLING = MAX_BLOCK_SIZE;
|
||||
/** Coinbase transaction outputs can only be spent after this number of new blocks (network rule) */
|
||||
extern int COINBASE_MATURITY;
|
||||
/** The minimum value which is invalid for expiry height, used by CTransaction and CMutableTransaction */
|
||||
|
||||
@@ -23,6 +23,7 @@ enum UpgradeIndex {
|
||||
BASE_SPROUT,
|
||||
UPGRADE_TESTDUMMY,
|
||||
UPGRADE_OVERWINTER,
|
||||
UPGRADE_SAPLING,
|
||||
// NOTE: Also add new upgrades to NetworkUpgradeInfo in upgrades.cpp
|
||||
MAX_NETWORK_UPGRADES
|
||||
};
|
||||
@@ -111,6 +112,7 @@ struct Params {
|
||||
int64_t AveragingWindowTimespan() const { return nPowAveragingWindow * nPowTargetSpacing; }
|
||||
int64_t MinActualTimespan() const { return (AveragingWindowTimespan() * (100 - nPowMaxAdjustUp )) / 100; }
|
||||
int64_t MaxActualTimespan() const { return (AveragingWindowTimespan() * (100 + nPowMaxAdjustDown)) / 100; }
|
||||
uint256 nMinimumChainWork;
|
||||
};
|
||||
} // namespace Consensus
|
||||
|
||||
|
||||
@@ -23,6 +23,11 @@ const struct NUInfo NetworkUpgradeInfo[Consensus::MAX_NETWORK_UPGRADES] = {
|
||||
/*.nBranchId =*/ 0x5ba81b19,
|
||||
/*.strName =*/ "Overwinter",
|
||||
/*.strInfo =*/ "See https://z.cash/upgrade/overwinter.html for details.",
|
||||
},
|
||||
{
|
||||
/*.nBranchId =*/ 0x76b809bb,
|
||||
/*.strName =*/ "Sapling",
|
||||
/*.strInfo =*/ "See https://z.cash/upgrade/sapling.html for details.",
|
||||
}
|
||||
};
|
||||
|
||||
@@ -69,6 +74,8 @@ int CurrentEpoch(int nHeight, const Consensus::Params& params) {
|
||||
return idxInt;
|
||||
}
|
||||
}
|
||||
// Base case
|
||||
return Consensus::BASE_SPROUT;
|
||||
}
|
||||
|
||||
uint32_t CurrentEpochBranchId(int nHeight, const Consensus::Params& params) {
|
||||
|
||||
@@ -16,6 +16,7 @@ class UniValue;
|
||||
|
||||
// core_read.cpp
|
||||
extern CScript ParseScript(const std::string& s);
|
||||
extern std::string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDecode = false);
|
||||
extern bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx);
|
||||
extern bool DecodeHexBlk(CBlock&, const std::string& strHexBlk);
|
||||
extern uint256 ParseHashUV(const UniValue& v, const std::string& strName);
|
||||
@@ -25,8 +26,7 @@ extern std::vector<unsigned char> ParseHexUV(const UniValue& v, const std::strin
|
||||
// core_write.cpp
|
||||
extern std::string FormatScript(const CScript& script);
|
||||
extern std::string EncodeHexTx(const CTransaction& tx);
|
||||
extern void ScriptPubKeyToUniv(const CScript& scriptPubKey,
|
||||
UniValue& out, bool fIncludeHex);
|
||||
extern void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex);
|
||||
extern void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry);
|
||||
|
||||
#endif // BITCOIN_CORE_IO_H
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "memusage.h"
|
||||
|
||||
static inline size_t RecursiveDynamicUsage(const CScript& script) {
|
||||
return memusage::DynamicUsage(*static_cast<const std::vector<unsigned char>*>(&script));
|
||||
return memusage::DynamicUsage(*static_cast<const CScriptBase*>(&script));
|
||||
}
|
||||
|
||||
static inline size_t RecursiveDynamicUsage(const COutPoint& out) {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include "core_io.h"
|
||||
|
||||
#include "base58.h"
|
||||
#include "key_io.h"
|
||||
#include "primitives/transaction.h"
|
||||
#include "script/script.h"
|
||||
#include "script/standard.h"
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "utilmoneystr.h"
|
||||
#include "utilstrencodings.h"
|
||||
|
||||
#include <boost/assign/list_of.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
using namespace std;
|
||||
@@ -54,6 +55,67 @@ string FormatScript(const CScript& script)
|
||||
return ret.substr(0, ret.size() - 1);
|
||||
}
|
||||
|
||||
const map<unsigned char, string> mapSigHashTypes =
|
||||
boost::assign::map_list_of
|
||||
(static_cast<unsigned char>(SIGHASH_ALL), string("ALL"))
|
||||
(static_cast<unsigned char>(SIGHASH_ALL|SIGHASH_ANYONECANPAY), string("ALL|ANYONECANPAY"))
|
||||
(static_cast<unsigned char>(SIGHASH_NONE), string("NONE"))
|
||||
(static_cast<unsigned char>(SIGHASH_NONE|SIGHASH_ANYONECANPAY), string("NONE|ANYONECANPAY"))
|
||||
(static_cast<unsigned char>(SIGHASH_SINGLE), string("SINGLE"))
|
||||
(static_cast<unsigned char>(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY), string("SINGLE|ANYONECANPAY"))
|
||||
;
|
||||
|
||||
/**
|
||||
* Create the assembly string representation of a CScript object.
|
||||
* @param[in] script CScript object to convert into the asm string representation.
|
||||
* @param[in] fAttemptSighashDecode Whether to attempt to decode sighash types on data within the script that matches the format
|
||||
* of a signature. Only pass true for scripts you believe could contain signatures. For example,
|
||||
* pass false, or omit the this argument (defaults to false), for scriptPubKeys.
|
||||
*/
|
||||
string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDecode)
|
||||
{
|
||||
string str;
|
||||
opcodetype opcode;
|
||||
vector<unsigned char> vch;
|
||||
CScript::const_iterator pc = script.begin();
|
||||
while (pc < script.end()) {
|
||||
if (!str.empty()) {
|
||||
str += " ";
|
||||
}
|
||||
if (!script.GetOp(pc, opcode, vch)) {
|
||||
str += "[error]";
|
||||
return str;
|
||||
}
|
||||
if (0 <= opcode && opcode <= OP_PUSHDATA4) {
|
||||
if (vch.size() <= static_cast<vector<unsigned char>::size_type>(4)) {
|
||||
str += strprintf("%d", CScriptNum(vch, false).getint());
|
||||
} else {
|
||||
// the IsUnspendable check makes sure not to try to decode OP_RETURN data that may match the format of a signature
|
||||
if (fAttemptSighashDecode && !script.IsUnspendable()) {
|
||||
string strSigHashDecode;
|
||||
// goal: only attempt to decode a defined sighash type from data that looks like a signature within a scriptSig.
|
||||
// this won't decode correctly formatted public keys in Pubkey or Multisig scripts due to
|
||||
// the restrictions on the pubkey formats (see IsCompressedOrUncompressedPubKey) being incongruous with the
|
||||
// checks in CheckSignatureEncoding.
|
||||
if (CheckSignatureEncoding(vch, SCRIPT_VERIFY_STRICTENC, NULL)) {
|
||||
const unsigned char chSigHashType = vch.back();
|
||||
if (mapSigHashTypes.count(chSigHashType)) {
|
||||
strSigHashDecode = "[" + mapSigHashTypes.find(chSigHashType)->second + "]";
|
||||
vch.pop_back(); // remove the sighash type byte. it will be replaced by the decode.
|
||||
}
|
||||
}
|
||||
str += HexStr(vch) + strSigHashDecode;
|
||||
} else {
|
||||
str += HexStr(vch);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
str += GetOpName(opcode);
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
string EncodeHexTx(const CTransaction& tx)
|
||||
{
|
||||
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
|
||||
@@ -68,7 +130,7 @@ void ScriptPubKeyToUniv(const CScript& scriptPubKey,
|
||||
vector<CTxDestination> addresses;
|
||||
int nRequired;
|
||||
|
||||
out.pushKV("asm", scriptPubKey.ToString());
|
||||
out.pushKV("asm", ScriptToAsmStr(scriptPubKey));
|
||||
if (fIncludeHex)
|
||||
out.pushKV("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end()));
|
||||
|
||||
@@ -81,8 +143,9 @@ void ScriptPubKeyToUniv(const CScript& scriptPubKey,
|
||||
out.pushKV("type", GetTxnOutputType(type));
|
||||
|
||||
UniValue a(UniValue::VARR);
|
||||
BOOST_FOREACH(const CTxDestination& addr, addresses)
|
||||
a.push_back(CBitcoinAddress(addr).ToString());
|
||||
for (const CTxDestination& addr : addresses) {
|
||||
a.push_back(EncodeDestination(addr));
|
||||
}
|
||||
out.pushKV("addresses", a);
|
||||
}
|
||||
|
||||
@@ -101,7 +164,7 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry)
|
||||
in.pushKV("txid", txin.prevout.hash.GetHex());
|
||||
in.pushKV("vout", (int64_t)txin.prevout.n);
|
||||
UniValue o(UniValue::VOBJ);
|
||||
o.pushKV("asm", txin.scriptSig.ToString());
|
||||
o.pushKV("asm", ScriptToAsmStr(txin.scriptSig, true));
|
||||
o.pushKV("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()));
|
||||
in.pushKV("scriptSig", o);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "sodium.h"
|
||||
#include "compat/endian.h"
|
||||
@@ -21,52 +22,67 @@
|
||||
|
||||
uint16_t static inline ReadLE16(const unsigned char* ptr)
|
||||
{
|
||||
return le16toh(*((uint16_t*)ptr));
|
||||
uint16_t x;
|
||||
memcpy((char*)&x, ptr, 2);
|
||||
return le16toh(x);
|
||||
}
|
||||
|
||||
uint32_t static inline ReadLE32(const unsigned char* ptr)
|
||||
{
|
||||
return le32toh(*((uint32_t*)ptr));
|
||||
uint32_t x;
|
||||
memcpy((char*)&x, ptr, 4);
|
||||
return le32toh(x);
|
||||
}
|
||||
|
||||
uint64_t static inline ReadLE64(const unsigned char* ptr)
|
||||
{
|
||||
return le64toh(*((uint64_t*)ptr));
|
||||
uint64_t x;
|
||||
memcpy((char*)&x, ptr, 8);
|
||||
return le64toh(x);
|
||||
}
|
||||
|
||||
void static inline WriteLE16(unsigned char* ptr, uint16_t x)
|
||||
{
|
||||
*((uint16_t*)ptr) = htole16(x);
|
||||
uint16_t v = htole16(x);
|
||||
memcpy(ptr, (char*)&v, 2);
|
||||
}
|
||||
|
||||
void static inline WriteLE32(unsigned char* ptr, uint32_t x)
|
||||
{
|
||||
*((uint32_t*)ptr) = htole32(x);
|
||||
uint32_t v = htole32(x);
|
||||
memcpy(ptr, (char*)&v, 4);
|
||||
}
|
||||
|
||||
void static inline WriteLE64(unsigned char* ptr, uint64_t x)
|
||||
{
|
||||
*((uint64_t*)ptr) = htole64(x);
|
||||
uint64_t v = htole64(x);
|
||||
memcpy(ptr, (char*)&v, 8);
|
||||
}
|
||||
|
||||
uint32_t static inline ReadBE32(const unsigned char* ptr)
|
||||
{
|
||||
return be32toh(*((uint32_t*)ptr));
|
||||
uint32_t x;
|
||||
memcpy((char*)&x, ptr, 4);
|
||||
return be32toh(x);
|
||||
}
|
||||
|
||||
uint64_t static inline ReadBE64(const unsigned char* ptr)
|
||||
{
|
||||
return be64toh(*((uint64_t*)ptr));
|
||||
uint64_t x;
|
||||
memcpy((char*)&x, ptr, 8);
|
||||
return be64toh(x);
|
||||
}
|
||||
|
||||
void static inline WriteBE32(unsigned char* ptr, uint32_t x)
|
||||
{
|
||||
*((uint32_t*)ptr) = htobe32(x);
|
||||
uint32_t v = htobe32(x);
|
||||
memcpy(ptr, (char*)&v, 4);
|
||||
}
|
||||
|
||||
void static inline WriteBE64(unsigned char* ptr, uint64_t x)
|
||||
{
|
||||
*((uint64_t*)ptr) = htobe64(x);
|
||||
uint64_t v = htobe64(x);
|
||||
memcpy(ptr, (char*)&v, 8);
|
||||
}
|
||||
|
||||
int inline init_and_check_sodium()
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
#define __BYTE_ORDER BYTE_ORDER
|
||||
#endif
|
||||
*/
|
||||
EhSolverCancelledException solver_cancelled;
|
||||
static EhSolverCancelledException solver_cancelled;
|
||||
|
||||
template<unsigned int N, unsigned int K>
|
||||
int Equihash<N,K>::InitialiseState(eh_HashState& base_state)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "leveldbwrapper.h"
|
||||
#include "dbwrapper.h"
|
||||
|
||||
#include "util.h"
|
||||
|
||||
@@ -12,20 +12,7 @@
|
||||
#include <leveldb/env.h>
|
||||
#include <leveldb/filter_policy.h>
|
||||
#include <memenv.h>
|
||||
|
||||
void HandleError(const leveldb::Status& status)
|
||||
{
|
||||
if (status.ok())
|
||||
return;
|
||||
LogPrintf("%s\n", status.ToString());
|
||||
if (status.IsCorruption())
|
||||
throw leveldb_error("Database corrupted");
|
||||
if (status.IsIOError())
|
||||
throw leveldb_error("Database I/O error");
|
||||
if (status.IsNotFound())
|
||||
throw leveldb_error("Database entry missing");
|
||||
throw leveldb_error("Unknown database error");
|
||||
}
|
||||
#include <stdint.h>
|
||||
|
||||
static leveldb::Options GetOptions(size_t nCacheSize, bool compression, int maxOpenFiles)
|
||||
{
|
||||
@@ -43,7 +30,7 @@ static leveldb::Options GetOptions(size_t nCacheSize, bool compression, int maxO
|
||||
return options;
|
||||
}
|
||||
|
||||
CLevelDBWrapper::CLevelDBWrapper(const boost::filesystem::path& path, size_t nCacheSize, bool fMemory, bool fWipe, bool compression, int maxOpenFiles)
|
||||
CDBWrapper::CDBWrapper(const boost::filesystem::path& path, size_t nCacheSize, bool fMemory, bool fWipe, bool compression, int maxOpenFiles)
|
||||
{
|
||||
penv = NULL;
|
||||
readoptions.verify_checksums = true;
|
||||
@@ -59,17 +46,17 @@ CLevelDBWrapper::CLevelDBWrapper(const boost::filesystem::path& path, size_t nCa
|
||||
if (fWipe) {
|
||||
LogPrintf("Wiping LevelDB in %s\n", path.string());
|
||||
leveldb::Status result = leveldb::DestroyDB(path.string(), options);
|
||||
HandleError(result);
|
||||
dbwrapper_private::HandleError(result);
|
||||
}
|
||||
TryCreateDirectory(path);
|
||||
LogPrintf("Opening LevelDB in %s\n", path.string());
|
||||
}
|
||||
leveldb::Status status = leveldb::DB::Open(options, path.string(), &pdb);
|
||||
HandleError(status);
|
||||
dbwrapper_private::HandleError(status);
|
||||
LogPrintf("Opened LevelDB successfully\n");
|
||||
}
|
||||
|
||||
CLevelDBWrapper::~CLevelDBWrapper()
|
||||
CDBWrapper::~CDBWrapper()
|
||||
{
|
||||
delete pdb;
|
||||
pdb = NULL;
|
||||
@@ -81,9 +68,41 @@ CLevelDBWrapper::~CLevelDBWrapper()
|
||||
options.env = NULL;
|
||||
}
|
||||
|
||||
bool CLevelDBWrapper::WriteBatch(CLevelDBBatch& batch, bool fSync)
|
||||
bool CDBWrapper::WriteBatch(CDBBatch& batch, bool fSync)
|
||||
{
|
||||
leveldb::Status status = pdb->Write(fSync ? syncoptions : writeoptions, &batch.batch);
|
||||
HandleError(status);
|
||||
dbwrapper_private::HandleError(status);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDBWrapper::IsEmpty()
|
||||
{
|
||||
boost::scoped_ptr<CDBIterator> it(NewIterator());
|
||||
it->SeekToFirst();
|
||||
return !(it->Valid());
|
||||
}
|
||||
|
||||
CDBIterator::~CDBIterator() { delete piter; }
|
||||
bool CDBIterator::Valid() { return piter->Valid(); }
|
||||
void CDBIterator::SeekToFirst() { piter->SeekToFirst(); }
|
||||
void CDBIterator::SeekToLast() { piter->SeekToLast(); }
|
||||
void CDBIterator::Next() { piter->Next(); }
|
||||
void CDBIterator::Prev() { piter->Prev(); }
|
||||
|
||||
namespace dbwrapper_private {
|
||||
|
||||
void HandleError(const leveldb::Status& status)
|
||||
{
|
||||
if (status.ok())
|
||||
return;
|
||||
LogPrintf("%s\n", status.ToString());
|
||||
if (status.IsCorruption())
|
||||
throw dbwrapper_error("Database corrupted");
|
||||
if (status.IsIOError())
|
||||
throw dbwrapper_error("Database I/O error");
|
||||
if (status.IsNotFound())
|
||||
throw dbwrapper_error("Database entry missing");
|
||||
throw dbwrapper_error("Unknown database error");
|
||||
}
|
||||
|
||||
};
|
||||
267
src/dbwrapper.h
Normal file
267
src/dbwrapper.h
Normal file
@@ -0,0 +1,267 @@
|
||||
// Copyright (c) 2012-2014 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef BITCOIN_DBWRAPPER_H
|
||||
#define BITCOIN_DBWRAPPER_H
|
||||
|
||||
#include "clientversion.h"
|
||||
#include "serialize.h"
|
||||
#include "streams.h"
|
||||
#include "util.h"
|
||||
#include "version.h"
|
||||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
#include <leveldb/db.h>
|
||||
#include <leveldb/write_batch.h>
|
||||
|
||||
static const size_t DBWRAPPER_PREALLOC_KEY_SIZE = 64;
|
||||
static const size_t DBWRAPPER_PREALLOC_VALUE_SIZE = 1024;
|
||||
|
||||
class dbwrapper_error : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
dbwrapper_error(const std::string& msg) : std::runtime_error(msg) {}
|
||||
};
|
||||
|
||||
class CDBWrapper;
|
||||
|
||||
/** These should be considered an implementation detail of the specific database.
|
||||
*/
|
||||
namespace dbwrapper_private {
|
||||
|
||||
/** Handle database error by throwing dbwrapper_error exception.
|
||||
*/
|
||||
void HandleError(const leveldb::Status& status);
|
||||
|
||||
};
|
||||
|
||||
/** Batch of changes queued to be written to a CDBWrapper */
|
||||
class CDBBatch
|
||||
{
|
||||
friend class CDBWrapper;
|
||||
|
||||
private:
|
||||
const CDBWrapper &parent;
|
||||
leveldb::WriteBatch batch;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @param[in] _parent CDBWrapper that this batch is to be submitted to
|
||||
*/
|
||||
CDBBatch(const CDBWrapper &_parent) : parent(_parent) { };
|
||||
|
||||
template <typename K, typename V>
|
||||
void Write(const K& key, const V& value)
|
||||
{
|
||||
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
|
||||
ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
|
||||
ssKey << key;
|
||||
leveldb::Slice slKey(&ssKey[0], ssKey.size());
|
||||
|
||||
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
|
||||
ssValue.reserve(DBWRAPPER_PREALLOC_VALUE_SIZE);
|
||||
ssValue << value;
|
||||
leveldb::Slice slValue(&ssValue[0], ssValue.size());
|
||||
|
||||
batch.Put(slKey, slValue);
|
||||
}
|
||||
|
||||
template <typename K>
|
||||
void Erase(const K& key)
|
||||
{
|
||||
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
|
||||
ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
|
||||
ssKey << key;
|
||||
leveldb::Slice slKey(&ssKey[0], ssKey.size());
|
||||
|
||||
batch.Delete(slKey);
|
||||
}
|
||||
};
|
||||
|
||||
class CDBIterator
|
||||
{
|
||||
private:
|
||||
const CDBWrapper &parent;
|
||||
leveldb::Iterator *piter;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* @param[in] _parent Parent CDBWrapper instance.
|
||||
* @param[in] _piter The original leveldb iterator.
|
||||
*/
|
||||
CDBIterator(const CDBWrapper &_parent, leveldb::Iterator *_piter) :
|
||||
parent(_parent), piter(_piter) { };
|
||||
~CDBIterator();
|
||||
|
||||
bool Valid();
|
||||
|
||||
void SeekToFirst();
|
||||
void SeekToLast();
|
||||
|
||||
template<typename K> void Seek(const K& key) {
|
||||
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
|
||||
ssKey.reserve(GetSerializeSize(ssKey, key));
|
||||
ssKey << key;
|
||||
leveldb::Slice slKey(&ssKey[0], ssKey.size());
|
||||
piter->Seek(slKey);
|
||||
}
|
||||
|
||||
void Next();
|
||||
void Prev();
|
||||
|
||||
template<typename K> bool GetKey(K& key) {
|
||||
leveldb::Slice slKey = piter->key();
|
||||
try {
|
||||
CDataStream ssKey(slKey.data(), slKey.data() + slKey.size(), SER_DISK, CLIENT_VERSION);
|
||||
ssKey >> key;
|
||||
} catch(std::exception &e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int GetKeySize() {
|
||||
return piter->key().size();
|
||||
}
|
||||
|
||||
template<typename V> bool GetValue(V& value) {
|
||||
leveldb::Slice slValue = piter->value();
|
||||
try {
|
||||
CDataStream ssValue(slValue.data(), slValue.data() + slValue.size(), SER_DISK, CLIENT_VERSION);
|
||||
ssValue >> value;
|
||||
} catch(std::exception &e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int GetValueSize() {
|
||||
return piter->value().size();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class CDBWrapper
|
||||
{
|
||||
private:
|
||||
//! custom environment this database is using (may be NULL in case of default environment)
|
||||
leveldb::Env* penv;
|
||||
|
||||
//! database options used
|
||||
leveldb::Options options;
|
||||
|
||||
//! options used when reading from the database
|
||||
leveldb::ReadOptions readoptions;
|
||||
|
||||
//! options used when iterating over values of the database
|
||||
leveldb::ReadOptions iteroptions;
|
||||
|
||||
//! options used when writing to the database
|
||||
leveldb::WriteOptions writeoptions;
|
||||
|
||||
//! options used when sync writing to the database
|
||||
leveldb::WriteOptions syncoptions;
|
||||
|
||||
//! the database itself
|
||||
leveldb::DB* pdb;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @param[in] path Location in the filesystem where leveldb data will be stored.
|
||||
* @param[in] nCacheSize Configures various leveldb cache settings.
|
||||
* @param[in] fMemory If true, use leveldb's memory environment.
|
||||
* @param[in] fWipe If true, remove all existing data.
|
||||
*/
|
||||
CDBWrapper(const boost::filesystem::path& path, size_t nCacheSize, bool fMemory = false, bool fWipe = false, bool compression = false, int maxOpenFiles = 64);
|
||||
~CDBWrapper();
|
||||
|
||||
template <typename K, typename V>
|
||||
bool Read(const K& key, V& value) const
|
||||
{
|
||||
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
|
||||
ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
|
||||
ssKey << key;
|
||||
leveldb::Slice slKey(&ssKey[0], ssKey.size());
|
||||
|
||||
std::string strValue;
|
||||
leveldb::Status status = pdb->Get(readoptions, slKey, &strValue);
|
||||
if (!status.ok()) {
|
||||
if (status.IsNotFound())
|
||||
return false;
|
||||
LogPrintf("LevelDB read failure: %s\n", status.ToString());
|
||||
dbwrapper_private::HandleError(status);
|
||||
}
|
||||
try {
|
||||
CDataStream ssValue(strValue.data(), strValue.data() + strValue.size(), SER_DISK, CLIENT_VERSION);
|
||||
ssValue >> value;
|
||||
} catch (const std::exception&) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename K, typename V>
|
||||
bool Write(const K& key, const V& value, bool fSync = false)
|
||||
{
|
||||
CDBBatch batch(*this);
|
||||
batch.Write(key, value);
|
||||
return WriteBatch(batch, fSync);
|
||||
}
|
||||
|
||||
template <typename K>
|
||||
bool Exists(const K& key) const
|
||||
{
|
||||
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
|
||||
ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
|
||||
ssKey << key;
|
||||
leveldb::Slice slKey(&ssKey[0], ssKey.size());
|
||||
|
||||
std::string strValue;
|
||||
leveldb::Status status = pdb->Get(readoptions, slKey, &strValue);
|
||||
if (!status.ok()) {
|
||||
if (status.IsNotFound())
|
||||
return false;
|
||||
LogPrintf("LevelDB read failure: %s\n", status.ToString());
|
||||
dbwrapper_private::HandleError(status);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename K>
|
||||
bool Erase(const K& key, bool fSync = false)
|
||||
{
|
||||
CDBBatch batch(*this);
|
||||
batch.Erase(key);
|
||||
return WriteBatch(batch, fSync);
|
||||
}
|
||||
|
||||
bool WriteBatch(CDBBatch& batch, bool fSync = false);
|
||||
|
||||
// not available for LevelDB; provide for compatibility with BDB
|
||||
bool Flush()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Sync()
|
||||
{
|
||||
CDBBatch batch(*this);
|
||||
return WriteBatch(batch, true);
|
||||
}
|
||||
|
||||
CDBIterator *NewIterator()
|
||||
{
|
||||
return new CDBIterator(*this, pdb->NewIterator(iteroptions));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the database managed by this class contains no entries.
|
||||
*/
|
||||
bool IsEmpty();
|
||||
};
|
||||
|
||||
#endif // BITCOIN_DBWRAPPER_H
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "deprecation.h"
|
||||
|
||||
#include "alert.h"
|
||||
#include "clientversion.h"
|
||||
#include "init.h"
|
||||
#include "ui_interface.h"
|
||||
@@ -13,14 +14,15 @@
|
||||
static const std::string CLIENT_VERSION_STR = FormatVersion(CLIENT_VERSION);
|
||||
extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN];
|
||||
|
||||
void EnforceNodeDeprecation(int nHeight, bool forceLogging) {
|
||||
void EnforceNodeDeprecation(int nHeight, bool forceLogging, bool fThread) {
|
||||
|
||||
// Do not enforce deprecation in regtest or on testnet
|
||||
std::string networkID = Params().NetworkIDString();
|
||||
std::string msg;
|
||||
|
||||
if (networkID != "main" || ASSETCHAINS_SYMBOL[0] != 0 ) return;
|
||||
|
||||
int blocksToDeprecation = DEPRECATION_HEIGHT - nHeight;
|
||||
bool disableDeprecation = (GetArg("-disabledeprecation", "") == CLIENT_VERSION_STR);
|
||||
if (blocksToDeprecation <= 0) {
|
||||
// In order to ensure we only log once per process when deprecation is
|
||||
// disabled (to avoid log spam), we only need to log in two cases:
|
||||
@@ -29,34 +31,20 @@ void EnforceNodeDeprecation(int nHeight, bool forceLogging) {
|
||||
// occurs, but that's an irregular event that won't cause spam.
|
||||
// - The node is starting
|
||||
if (blocksToDeprecation == 0 || forceLogging) {
|
||||
auto msg = strprintf(_("This version has been deprecated as of block height %d."),
|
||||
msg = strprintf(_("This version has been deprecated as of block height %d."),
|
||||
DEPRECATION_HEIGHT) + " " +
|
||||
_("You should upgrade to the latest version of Komodo.");
|
||||
if (!disableDeprecation) {
|
||||
msg += " " + strprintf(_("To disable deprecation for this version, set %s%s."),
|
||||
"-disabledeprecation=", CLIENT_VERSION_STR);
|
||||
}
|
||||
LogPrintf("*** %s\n", msg);
|
||||
CAlert::Notify(msg, fThread);
|
||||
uiInterface.ThreadSafeMessageBox(msg, "", CClientUIInterface::MSG_ERROR);
|
||||
}
|
||||
if (!disableDeprecation) {
|
||||
StartShutdown();
|
||||
}
|
||||
} else if (blocksToDeprecation == DEPRECATION_WARN_LIMIT ||
|
||||
(blocksToDeprecation < DEPRECATION_WARN_LIMIT && forceLogging)) {
|
||||
std::string msg;
|
||||
if (disableDeprecation) {
|
||||
msg = strprintf(_("This version will be deprecated at block height %d."),
|
||||
StartShutdown();
|
||||
} else if (blocksToDeprecation == DEPRECATION_WARN_LIMIT || (blocksToDeprecation < DEPRECATION_WARN_LIMIT && forceLogging)) {
|
||||
msg = strprintf(_("This version will be deprecated at block height %d, and will automatically shut down."),
|
||||
DEPRECATION_HEIGHT) + " " +
|
||||
_("You should upgrade to the latest version of Komodo.");
|
||||
} else {
|
||||
msg = strprintf(_("This version will be deprecated at block height %d, and will automatically shut down."),
|
||||
DEPRECATION_HEIGHT) + " " +
|
||||
_("You should upgrade to the latest version of Komodo.") + " " +
|
||||
strprintf(_("To disable deprecation for this version, set %s%s."),
|
||||
"-disabledeprecation=", CLIENT_VERSION_STR);
|
||||
}
|
||||
LogPrintf("*** %s\n", msg);
|
||||
uiInterface.ThreadSafeMessageBox(msg, "", CClientUIInterface::MSG_WARNING);
|
||||
}
|
||||
LogPrintf("*** %s\n", msg);
|
||||
CAlert::Notify(msg, fThread);
|
||||
uiInterface.ThreadSafeMessageBox(msg, "", CClientUIInterface::MSG_WARNING);
|
||||
}
|
||||
|
||||
@@ -18,8 +18,11 @@ static const int DEPRECATION_WARN_LIMIT = 60 * 24 * 60; // 2 months
|
||||
/**
|
||||
* Checks whether the node is deprecated based on the current block height, and
|
||||
* shuts down the node with an error if so (and deprecation is not disabled for
|
||||
* the current client version).
|
||||
* the current client version). Warning and error messages are sent to the debug
|
||||
* log, the metrics UI, and (if configured) -alertnofity.
|
||||
*
|
||||
* fThread means run -alertnotify in a free-running thread.
|
||||
*/
|
||||
void EnforceNodeDeprecation(int nHeight, bool forceLogging=false);
|
||||
void EnforceNodeDeprecation(int nHeight, bool forceLogging=false, bool fThread=true);
|
||||
|
||||
#endif // ZCASH_DEPRECATION_H
|
||||
|
||||
@@ -26,7 +26,7 @@ void expect_deser_same(const T& expected)
|
||||
CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss2 << object;
|
||||
|
||||
ASSERT_TRUE(serialized_size == ss2.size());
|
||||
ASSERT_EQ(serialized_size, ss2.size());
|
||||
ASSERT_TRUE(memcmp(&*ss1.begin(), &*ss2.begin(), serialized_size) == 0);
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ void expect_test_vector(T& v, const U& expected)
|
||||
std::string raw = v.get_str();
|
||||
CDataStream ss2(ParseHex(raw), SER_NETWORK, PROTOCOL_VERSION);
|
||||
|
||||
ASSERT_TRUE(ss1.size() == ss2.size());
|
||||
ASSERT_EQ(ss1.size(), ss2.size());
|
||||
ASSERT_TRUE(memcmp(&*ss1.begin(), &*ss2.begin(), ss1.size()) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "gmock/gmock.h"
|
||||
#include "crypto/common.h"
|
||||
#include "key.h"
|
||||
#include "pubkey.h"
|
||||
#include "zcash/JoinSplit.hpp"
|
||||
#include "util.h"
|
||||
@@ -7,6 +8,8 @@
|
||||
#include <libsnark/common/default_types/r1cs_ppzksnark_pp.hpp>
|
||||
#include <libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp>
|
||||
|
||||
#include "librustzcash.h"
|
||||
|
||||
struct ECCryptoClosure
|
||||
{
|
||||
ECCVerifyHandle handle;
|
||||
@@ -18,13 +21,36 @@ ZCJoinSplit* params;
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
assert(init_and_check_sodium() != -1);
|
||||
ECC_Start();
|
||||
|
||||
libsnark::default_r1cs_ppzksnark_pp::init_public_params();
|
||||
libsnark::inhibit_profiling_info = true;
|
||||
libsnark::inhibit_profiling_counters = true;
|
||||
boost::filesystem::path pk_path = ZC_GetParamsDir() / "sprout-proving.key";
|
||||
boost::filesystem::path vk_path = ZC_GetParamsDir() / "sprout-verifying.key";
|
||||
params = ZCJoinSplit::Prepared(vk_path.string(), pk_path.string());
|
||||
|
||||
boost::filesystem::path sapling_spend = ZC_GetParamsDir() / "sapling-spend.params";
|
||||
boost::filesystem::path sapling_output = ZC_GetParamsDir() / "sapling-output.params";
|
||||
boost::filesystem::path sprout_groth16 = ZC_GetParamsDir() / "sprout-groth16.params";
|
||||
|
||||
std::string sapling_spend_str = sapling_spend.string();
|
||||
std::string sapling_output_str = sapling_output.string();
|
||||
std::string sprout_groth16_str = sprout_groth16.string();
|
||||
|
||||
librustzcash_init_zksnark_params(
|
||||
sapling_spend_str.c_str(),
|
||||
"8270785a1a0d0bc77196f000ee6d221c9c9894f55307bd9357c3f0105d31ca63991ab91324160d8f53e2bbd3c2633a6eb8bdf5205d822e7f3f73edac51b2b70c",
|
||||
sapling_output_str.c_str(),
|
||||
"657e3d38dbb5cb5e7dd2970e8b03d69b4787dd907285b5a7f0790dcc8072f60bf593b32cc2d1c030e00ff5ae64bf84c5c3beb84ddc841d48264b4a171744d028",
|
||||
sprout_groth16_str.c_str(),
|
||||
"e9b238411bd6c0ec4791e9d04245ec350c9c5744f5610dfcce4365d5ca49dfefd5054e371842b3f88fa1b9d7e8e075249b3ebabd167fa8b0f3161292d36c180a"
|
||||
);
|
||||
|
||||
testing::InitGoogleMock(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
|
||||
auto ret = RUN_ALL_TESTS();
|
||||
|
||||
ECC_Stop();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -68,27 +68,99 @@ TEST(CheckBlock, BlockSproutRejectsBadVersion) {
|
||||
}
|
||||
|
||||
|
||||
TEST(ContextualCheckBlock, BadCoinbaseHeight) {
|
||||
SelectParams(CBaseChainParams::MAIN);
|
||||
class ContextualCheckBlockTest : public ::testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
SelectParams(CBaseChainParams::MAIN);
|
||||
}
|
||||
|
||||
// Create a block with no height in scriptSig
|
||||
CMutableTransaction mtx;
|
||||
mtx.vin.resize(1);
|
||||
mtx.vin[0].prevout.SetNull();
|
||||
virtual void TearDown() {
|
||||
// Revert to test default. No-op on mainnet params.
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
|
||||
}
|
||||
|
||||
// Returns a valid but empty mutable transaction at block height 1.
|
||||
CMutableTransaction GetFirstBlockCoinbaseTx() {
|
||||
CMutableTransaction mtx;
|
||||
|
||||
// No inputs.
|
||||
mtx.vin.resize(1);
|
||||
mtx.vin[0].prevout.SetNull();
|
||||
|
||||
// Set height to 1.
|
||||
mtx.vin[0].scriptSig = CScript() << 1 << OP_0;
|
||||
|
||||
// Give it a single zero-valued, always-valid output.
|
||||
mtx.vout.resize(1);
|
||||
mtx.vout[0].scriptPubKey = CScript() << OP_TRUE;
|
||||
mtx.vout[0].nValue = 0;
|
||||
|
||||
// Give it a Founder's Reward vout for height 1.
|
||||
mtx.vout.push_back(CTxOut(
|
||||
GetBlockSubsidy(1, Params().GetConsensus())/5,
|
||||
Params().GetFoundersRewardScriptAtHeight(1)));
|
||||
|
||||
return mtx;
|
||||
}
|
||||
|
||||
// Expects a height-1 block containing a given transaction to pass
|
||||
// ContextualCheckBlock. This is used in accepting (Sprout-Sprout,
|
||||
// Overwinter-Overwinter, ...) tests. You should not call it without
|
||||
// calling a SCOPED_TRACE macro first to usefully label any failures.
|
||||
void ExpectValidBlockFromTx(const CTransaction& tx) {
|
||||
// Create a block and add the transaction to it.
|
||||
CBlock block;
|
||||
block.vtx.push_back(tx);
|
||||
|
||||
// Set the previous block index to the genesis block.
|
||||
CBlockIndex indexPrev {Params().GenesisBlock()};
|
||||
|
||||
// We now expect this to be a valid block.
|
||||
MockCValidationState state;
|
||||
EXPECT_TRUE(ContextualCheckBlock(block, state, &indexPrev));
|
||||
}
|
||||
|
||||
// Expects a height-1 block containing a given transaction to fail
|
||||
// ContextualCheckBlock. This is used in rejecting (Sprout-Overwinter,
|
||||
// Overwinter-Sprout, ...) tests. You should not call it without
|
||||
// calling a SCOPED_TRACE macro first to usefully label any failures.
|
||||
void ExpectInvalidBlockFromTx(const CTransaction& tx, int level, std::string reason) {
|
||||
// Create a block and add the transaction to it.
|
||||
CBlock block;
|
||||
block.vtx.push_back(tx);
|
||||
|
||||
// Set the previous block index to the genesis block.
|
||||
CBlockIndex indexPrev {Params().GenesisBlock()};
|
||||
|
||||
// We now expect this to be an invalid block, for the given reason.
|
||||
MockCValidationState state;
|
||||
EXPECT_CALL(state, DoS(level, false, REJECT_INVALID, reason, false)).Times(1);
|
||||
EXPECT_FALSE(ContextualCheckBlock(block, state, &indexPrev));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
TEST_F(ContextualCheckBlockTest, BadCoinbaseHeight) {
|
||||
// Put a transaction in a block with no height in scriptSig
|
||||
CMutableTransaction mtx = GetFirstBlockCoinbaseTx();
|
||||
mtx.vin[0].scriptSig = CScript() << OP_0;
|
||||
mtx.vout.resize(1);
|
||||
mtx.vout[0].scriptPubKey = CScript() << OP_TRUE;
|
||||
mtx.vout[0].nValue = 0;
|
||||
CTransaction tx {mtx};
|
||||
mtx.vout.pop_back(); // remove the FR output
|
||||
|
||||
CBlock block;
|
||||
block.vtx.push_back(tx);
|
||||
block.vtx.push_back(mtx);
|
||||
|
||||
// Treating block as genesis should pass
|
||||
MockCValidationState state;
|
||||
EXPECT_TRUE(ContextualCheckBlock(block, state, NULL));
|
||||
|
||||
// Give the transaction a Founder's Reward vout
|
||||
mtx.vout.push_back(CTxOut(
|
||||
GetBlockSubsidy(1, Params().GetConsensus())/5,
|
||||
Params().GetFoundersRewardScriptAtHeight(1)));
|
||||
|
||||
// Treating block as non-genesis should fail
|
||||
mtx.vout.push_back(CTxOut(GetBlockSubsidy(1, Params().GetConsensus())/5, Params().GetFoundersRewardScriptAtHeight(1)));
|
||||
CTransaction tx2 {mtx};
|
||||
block.vtx[0] = tx2;
|
||||
CBlock prev;
|
||||
@@ -111,124 +183,142 @@ TEST(ContextualCheckBlock, BadCoinbaseHeight) {
|
||||
EXPECT_TRUE(ContextualCheckBlock(block, state, &indexPrev));
|
||||
}
|
||||
|
||||
// Test that a block evaluated under Sprout rules cannot contain Overwinter transactions.
|
||||
// TEST PLAN: first, check that each ruleset accepts its own transaction type.
|
||||
// Currently (May 2018) this means we'll test Sprout-Sprout,
|
||||
// Overwinter-Overwinter, and Sapling-Sapling.
|
||||
|
||||
// Test block evaluated under Sprout rules will accept Sprout transactions.
|
||||
// This test assumes that mainnet Overwinter activation is at least height 2.
|
||||
TEST(ContextualCheckBlock, BlockSproutRulesRejectOverwinterTx) {
|
||||
SelectParams(CBaseChainParams::MAIN);
|
||||
TEST_F(ContextualCheckBlockTest, BlockSproutRulesAcceptSproutTx) {
|
||||
CMutableTransaction mtx = GetFirstBlockCoinbaseTx();
|
||||
|
||||
CMutableTransaction mtx;
|
||||
mtx.vin.resize(1);
|
||||
mtx.vin[0].prevout.SetNull();
|
||||
mtx.vin[0].scriptSig = CScript() << 1 << OP_0;
|
||||
mtx.vout.resize(1);
|
||||
mtx.vout[0].scriptPubKey = CScript() << OP_TRUE;
|
||||
mtx.vout[0].nValue = 0;
|
||||
|
||||
mtx.fOverwintered = true;
|
||||
mtx.nVersion = 3;
|
||||
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
|
||||
|
||||
CTransaction tx {mtx};
|
||||
CBlock block;
|
||||
block.vtx.push_back(tx);
|
||||
|
||||
MockCValidationState state;
|
||||
CBlockIndex indexPrev {Params().GenesisBlock()};
|
||||
|
||||
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "tx-overwinter-not-active", false)).Times(1);
|
||||
EXPECT_FALSE(ContextualCheckBlock(block, state, &indexPrev));
|
||||
}
|
||||
|
||||
|
||||
// Test block evaluated under Sprout rules will accept Sprout transactions
|
||||
TEST(ContextualCheckBlock, BlockSproutRulesAcceptSproutTx) {
|
||||
SelectParams(CBaseChainParams::REGTEST);
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
|
||||
|
||||
CMutableTransaction mtx;
|
||||
mtx.vin.resize(1);
|
||||
mtx.vin[0].prevout.SetNull();
|
||||
mtx.vin[0].scriptSig = CScript() << 1 << OP_0;
|
||||
mtx.vout.resize(1);
|
||||
mtx.vout[0].scriptPubKey = CScript() << OP_TRUE;
|
||||
mtx.vout[0].nValue = 0;
|
||||
mtx.vout.push_back(CTxOut(
|
||||
GetBlockSubsidy(1, Params().GetConsensus())/5,
|
||||
Params().GetFoundersRewardScriptAtHeight(1)));
|
||||
// Make it a Sprout transaction w/o JoinSplits
|
||||
mtx.fOverwintered = false;
|
||||
mtx.nVersion = 1;
|
||||
|
||||
CTransaction tx {mtx};
|
||||
CBlock block;
|
||||
block.vtx.push_back(tx);
|
||||
MockCValidationState state;
|
||||
CBlockIndex indexPrev {Params().GenesisBlock()};
|
||||
|
||||
EXPECT_TRUE(ContextualCheckBlock(block, state, &indexPrev));
|
||||
|
||||
// Revert to default
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
|
||||
SCOPED_TRACE("BlockSproutRulesAcceptSproutTx");
|
||||
ExpectValidBlockFromTx(CTransaction(mtx));
|
||||
}
|
||||
|
||||
|
||||
// Test block evaluated under Overwinter rules will accept Overwinter transactions
|
||||
TEST(ContextualCheckBlock, BlockOverwinterRulesAcceptOverwinterTx) {
|
||||
// Test block evaluated under Overwinter rules will accept Overwinter transactions.
|
||||
TEST_F(ContextualCheckBlockTest, BlockOverwinterRulesAcceptOverwinterTx) {
|
||||
SelectParams(CBaseChainParams::REGTEST);
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, 1);
|
||||
|
||||
CMutableTransaction mtx;
|
||||
mtx.vin.resize(1);
|
||||
mtx.vin[0].prevout.SetNull();
|
||||
mtx.vin[0].scriptSig = CScript() << 1 << OP_0;
|
||||
mtx.vout.resize(1);
|
||||
mtx.vout[0].scriptPubKey = CScript() << OP_TRUE;
|
||||
mtx.vout[0].nValue = 0;
|
||||
mtx.vout.push_back(CTxOut(
|
||||
GetBlockSubsidy(1, Params().GetConsensus())/5,
|
||||
Params().GetFoundersRewardScriptAtHeight(1)));
|
||||
CMutableTransaction mtx = GetFirstBlockCoinbaseTx();
|
||||
|
||||
// Make it an Overwinter transaction
|
||||
mtx.fOverwintered = true;
|
||||
mtx.nVersion = 3;
|
||||
mtx.nVersion = OVERWINTER_TX_VERSION;
|
||||
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
|
||||
|
||||
CTransaction tx {mtx};
|
||||
CBlock block;
|
||||
block.vtx.push_back(tx);
|
||||
MockCValidationState state;
|
||||
CBlockIndex indexPrev {Params().GenesisBlock()};
|
||||
|
||||
EXPECT_TRUE(ContextualCheckBlock(block, state, &indexPrev));
|
||||
|
||||
// Revert to default
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
|
||||
SCOPED_TRACE("BlockOverwinterRulesAcceptOverwinterTx");
|
||||
ExpectValidBlockFromTx(CTransaction(mtx));
|
||||
}
|
||||
|
||||
|
||||
// Test that a block evaluated under Sapling rules can contain Sapling transactions.
|
||||
TEST_F(ContextualCheckBlockTest, BlockSaplingRulesAcceptSaplingTx) {
|
||||
SelectParams(CBaseChainParams::REGTEST);
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, 1);
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, 1);
|
||||
|
||||
// Test block evaluated under Overwinter rules will reject Sprout transactions
|
||||
TEST(ContextualCheckBlock, BlockOverwinterRulesRejectSproutTx) {
|
||||
CMutableTransaction mtx = GetFirstBlockCoinbaseTx();
|
||||
|
||||
// Make it a Sapling transaction
|
||||
mtx.fOverwintered = true;
|
||||
mtx.nVersion = SAPLING_TX_VERSION;
|
||||
mtx.nVersionGroupId = SAPLING_VERSION_GROUP_ID;
|
||||
|
||||
SCOPED_TRACE("BlockSaplingRulesAcceptSaplingTx");
|
||||
ExpectValidBlockFromTx(CTransaction(mtx));
|
||||
}
|
||||
|
||||
// TEST PLAN: next, check that each ruleset will not accept other transaction
|
||||
// types. Currently (May 2018) this means we'll test Sprout-Overwinter,
|
||||
// Sprout-Sapling, Overwinter-Sprout, Overwinter-Sapling, Sapling-Sprout, and
|
||||
// Sapling-Overwinter.
|
||||
|
||||
// Test that a block evaluated under Sprout rules cannot contain non-Sprout
|
||||
// transactions which require Overwinter to be active. This test assumes that
|
||||
// mainnet Overwinter activation is at least height 2.
|
||||
TEST_F(ContextualCheckBlockTest, BlockSproutRulesRejectOtherTx) {
|
||||
CMutableTransaction mtx = GetFirstBlockCoinbaseTx();
|
||||
|
||||
// Make it an Overwinter transaction
|
||||
mtx.fOverwintered = true;
|
||||
mtx.nVersion = OVERWINTER_TX_VERSION;
|
||||
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
|
||||
|
||||
{
|
||||
SCOPED_TRACE("BlockSproutRulesRejectOverwinterTx");
|
||||
ExpectInvalidBlockFromTx(CTransaction(mtx), 0, "tx-overwinter-not-active");
|
||||
}
|
||||
|
||||
// Make it a Sapling transaction
|
||||
mtx.fOverwintered = true;
|
||||
mtx.nVersion = SAPLING_TX_VERSION;
|
||||
mtx.nVersionGroupId = SAPLING_VERSION_GROUP_ID;
|
||||
|
||||
{
|
||||
SCOPED_TRACE("BlockSproutRulesRejectSaplingTx");
|
||||
ExpectInvalidBlockFromTx(CTransaction(mtx), 0, "tx-overwinter-not-active");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Test block evaluated under Overwinter rules cannot contain non-Overwinter
|
||||
// transactions.
|
||||
TEST_F(ContextualCheckBlockTest, BlockOverwinterRulesRejectOtherTx) {
|
||||
SelectParams(CBaseChainParams::REGTEST);
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, 1);
|
||||
|
||||
CMutableTransaction mtx;
|
||||
mtx.vin.resize(1);
|
||||
mtx.vin[0].prevout.SetNull();
|
||||
mtx.vin[0].scriptSig = CScript() << 1 << OP_0;
|
||||
mtx.vout.resize(1);
|
||||
mtx.vout[0].scriptPubKey = CScript() << OP_TRUE;
|
||||
mtx.vout[0].nValue = 0;
|
||||
CMutableTransaction mtx = GetFirstBlockCoinbaseTx();
|
||||
|
||||
// Set the version to Sprout+JoinSplit (but nJoinSplit will be 0).
|
||||
mtx.nVersion = 2;
|
||||
|
||||
CTransaction tx {mtx};
|
||||
CBlock block;
|
||||
block.vtx.push_back(tx);
|
||||
{
|
||||
SCOPED_TRACE("BlockOverwinterRulesRejectSproutTx");
|
||||
ExpectInvalidBlockFromTx(CTransaction(mtx), 100, "tx-overwinter-active");
|
||||
}
|
||||
|
||||
MockCValidationState state;
|
||||
CBlockIndex indexPrev {Params().GenesisBlock()};
|
||||
// Make it a Sapling transaction
|
||||
mtx.fOverwintered = true;
|
||||
mtx.nVersion = SAPLING_TX_VERSION;
|
||||
mtx.nVersionGroupId = SAPLING_VERSION_GROUP_ID;
|
||||
|
||||
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "tx-overwinter-active", false)).Times(1);
|
||||
EXPECT_FALSE(ContextualCheckBlock(block, state, &indexPrev));
|
||||
|
||||
// Revert to default
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
|
||||
{
|
||||
SCOPED_TRACE("BlockOverwinterRulesRejectSaplingTx");
|
||||
ExpectInvalidBlockFromTx(CTransaction(mtx), 100, "bad-overwinter-tx-version-group-id");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Test block evaluated under Sapling rules cannot contain non-Sapling transactions.
|
||||
TEST_F(ContextualCheckBlockTest, BlockSaplingRulesRejectOtherTx) {
|
||||
SelectParams(CBaseChainParams::REGTEST);
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, 1);
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, 1);
|
||||
|
||||
CMutableTransaction mtx = GetFirstBlockCoinbaseTx();
|
||||
|
||||
// Set the version to Sprout+JoinSplit (but nJoinSplit will be 0).
|
||||
mtx.nVersion = 2;
|
||||
|
||||
{
|
||||
SCOPED_TRACE("BlockSaplingRulesRejectSproutTx");
|
||||
ExpectInvalidBlockFromTx(CTransaction(mtx), 100, "tx-overwinter-active");
|
||||
}
|
||||
|
||||
// Make it an Overwinter transaction
|
||||
mtx.fOverwintered = true;
|
||||
mtx.nVersion = OVERWINTER_TX_VERSION;
|
||||
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
|
||||
|
||||
{
|
||||
SCOPED_TRACE("BlockSaplingRulesRejectOverwinterTx");
|
||||
ExpectInvalidBlockFromTx(CTransaction(mtx), 100, "bad-sapling-tx-version-group-id");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include "primitives/transaction.h"
|
||||
#include "consensus/validation.h"
|
||||
|
||||
extern ZCJoinSplit* params;
|
||||
|
||||
TEST(checktransaction_tests, check_vpub_not_both_nonzero) {
|
||||
CMutableTransaction tx;
|
||||
tx.nVersion = 2;
|
||||
@@ -43,6 +45,7 @@ public:
|
||||
MOCK_CONST_METHOD0(GetRejectReason, std::string());
|
||||
};
|
||||
|
||||
void CreateJoinSplitSignature(CMutableTransaction& mtx, uint32_t consensusBranchId);
|
||||
|
||||
CMutableTransaction GetValidTransaction() {
|
||||
uint32_t consensusBranchId = SPROUT_BRANCH_ID;
|
||||
@@ -63,7 +66,11 @@ CMutableTransaction GetValidTransaction() {
|
||||
mtx.vjoinsplit[1].nullifiers.at(0) = uint256S("0000000000000000000000000000000000000000000000000000000000000002");
|
||||
mtx.vjoinsplit[1].nullifiers.at(1) = uint256S("0000000000000000000000000000000000000000000000000000000000000003");
|
||||
|
||||
CreateJoinSplitSignature(mtx, consensusBranchId);
|
||||
return mtx;
|
||||
}
|
||||
|
||||
void CreateJoinSplitSignature(CMutableTransaction& mtx, uint32_t consensusBranchId) {
|
||||
// Generate an ephemeral keypair.
|
||||
uint256 joinSplitPubKey;
|
||||
unsigned char joinSplitPrivKey[crypto_sign_SECRETKEYBYTES];
|
||||
@@ -86,7 +93,6 @@ CMutableTransaction GetValidTransaction() {
|
||||
dataToBeSigned.begin(), 32,
|
||||
joinSplitPrivKey
|
||||
) == 0);
|
||||
return mtx;
|
||||
}
|
||||
|
||||
TEST(checktransaction_tests, valid_transaction) {
|
||||
@@ -129,7 +135,8 @@ TEST(checktransaction_tests, bad_txns_vout_empty) {
|
||||
CheckTransactionWithoutProofVerification(tx, state);
|
||||
}
|
||||
|
||||
TEST(checktransaction_tests, bad_txns_oversize) {
|
||||
TEST(checktransaction_tests, BadTxnsOversize) {
|
||||
SelectParams(CBaseChainParams::REGTEST);
|
||||
CMutableTransaction mtx = GetValidTransaction();
|
||||
|
||||
mtx.vin[0].scriptSig = CScript();
|
||||
@@ -153,10 +160,100 @@ TEST(checktransaction_tests, bad_txns_oversize) {
|
||||
CTransaction tx(mtx);
|
||||
ASSERT_EQ(::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION), 100202);
|
||||
|
||||
// Passes non-contextual checks...
|
||||
MockCValidationState state;
|
||||
EXPECT_TRUE(CheckTransactionWithoutProofVerification(tx, state));
|
||||
|
||||
// ... but fails contextual ones!
|
||||
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-oversize", false)).Times(1);
|
||||
EXPECT_FALSE(ContextualCheckTransaction(tx, state, 1, 100));
|
||||
}
|
||||
|
||||
{
|
||||
// But should be fine again once Sapling activates!
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
|
||||
|
||||
mtx.fOverwintered = true;
|
||||
mtx.nVersionGroupId = SAPLING_VERSION_GROUP_ID;
|
||||
mtx.nVersion = SAPLING_TX_VERSION;
|
||||
|
||||
// Change the proof types (which requires re-signing the JoinSplit data)
|
||||
mtx.vjoinsplit[0].proof = libzcash::GrothProof();
|
||||
mtx.vjoinsplit[1].proof = libzcash::GrothProof();
|
||||
CreateJoinSplitSignature(mtx, NetworkUpgradeInfo[Consensus::UPGRADE_SAPLING].nBranchId);
|
||||
|
||||
CTransaction tx(mtx);
|
||||
EXPECT_EQ(::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION), 103713);
|
||||
|
||||
MockCValidationState state;
|
||||
EXPECT_TRUE(CheckTransactionWithoutProofVerification(tx, state));
|
||||
EXPECT_TRUE(ContextualCheckTransaction(tx, state, 1, 100));
|
||||
|
||||
// Revert to default
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(checktransaction_tests, OversizeSaplingTxns) {
|
||||
SelectParams(CBaseChainParams::REGTEST);
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
|
||||
|
||||
CMutableTransaction mtx = GetValidTransaction();
|
||||
mtx.fOverwintered = true;
|
||||
mtx.nVersionGroupId = SAPLING_VERSION_GROUP_ID;
|
||||
mtx.nVersion = SAPLING_TX_VERSION;
|
||||
|
||||
// Change the proof types (which requires re-signing the JoinSplit data)
|
||||
mtx.vjoinsplit[0].proof = libzcash::GrothProof();
|
||||
mtx.vjoinsplit[1].proof = libzcash::GrothProof();
|
||||
CreateJoinSplitSignature(mtx, NetworkUpgradeInfo[Consensus::UPGRADE_SAPLING].nBranchId);
|
||||
|
||||
// Transaction just under the limit
|
||||
mtx.vin[0].scriptSig = CScript();
|
||||
std::vector<unsigned char> vchData(520);
|
||||
for (unsigned int i = 0; i < 3809; ++i)
|
||||
mtx.vin[0].scriptSig << vchData << OP_DROP;
|
||||
std::vector<unsigned char> vchDataRemainder(453);
|
||||
mtx.vin[0].scriptSig << vchDataRemainder << OP_DROP;
|
||||
mtx.vin[0].scriptSig << OP_1;
|
||||
|
||||
{
|
||||
CTransaction tx(mtx);
|
||||
EXPECT_EQ(::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION), MAX_TX_SIZE_AFTER_SAPLING - 1);
|
||||
|
||||
CValidationState state;
|
||||
EXPECT_TRUE(CheckTransactionWithoutProofVerification(tx, state));
|
||||
}
|
||||
|
||||
// Transaction equal to the limit
|
||||
mtx.vin[1].scriptSig << OP_1;
|
||||
|
||||
{
|
||||
CTransaction tx(mtx);
|
||||
EXPECT_EQ(::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION), MAX_TX_SIZE_AFTER_SAPLING);
|
||||
|
||||
CValidationState state;
|
||||
EXPECT_TRUE(CheckTransactionWithoutProofVerification(tx, state));
|
||||
}
|
||||
|
||||
// Transaction just over the limit
|
||||
mtx.vin[1].scriptSig << OP_1;
|
||||
|
||||
{
|
||||
CTransaction tx(mtx);
|
||||
EXPECT_EQ(::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION), MAX_TX_SIZE_AFTER_SAPLING + 1);
|
||||
|
||||
MockCValidationState state;
|
||||
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-oversize", false)).Times(1);
|
||||
CheckTransactionWithoutProofVerification(tx, state);
|
||||
EXPECT_FALSE(CheckTransactionWithoutProofVerification(tx, state));
|
||||
}
|
||||
|
||||
// Revert to default
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
|
||||
}
|
||||
|
||||
TEST(checktransaction_tests, bad_txns_vout_negative) {
|
||||
@@ -193,6 +290,54 @@ TEST(checktransaction_tests, bad_txns_txouttotal_toolarge_outputs) {
|
||||
CheckTransactionWithoutProofVerification(tx, state);
|
||||
}
|
||||
|
||||
TEST(checktransaction_tests, ValueBalanceNonZero) {
|
||||
CMutableTransaction mtx = GetValidTransaction();
|
||||
mtx.valueBalance = 10;
|
||||
|
||||
CTransaction tx(mtx);
|
||||
|
||||
MockCValidationState state;
|
||||
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-valuebalance-nonzero", false)).Times(1);
|
||||
CheckTransactionWithoutProofVerification(tx, state);
|
||||
}
|
||||
|
||||
TEST(checktransaction_tests, PositiveValueBalanceTooLarge) {
|
||||
CMutableTransaction mtx = GetValidTransaction();
|
||||
mtx.vShieldedSpend.resize(1);
|
||||
mtx.valueBalance = MAX_MONEY + 1;
|
||||
|
||||
CTransaction tx(mtx);
|
||||
|
||||
MockCValidationState state;
|
||||
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-valuebalance-toolarge", false)).Times(1);
|
||||
CheckTransactionWithoutProofVerification(tx, state);
|
||||
}
|
||||
|
||||
TEST(checktransaction_tests, NegativeValueBalanceTooLarge) {
|
||||
CMutableTransaction mtx = GetValidTransaction();
|
||||
mtx.vShieldedSpend.resize(1);
|
||||
mtx.valueBalance = -(MAX_MONEY + 1);
|
||||
|
||||
CTransaction tx(mtx);
|
||||
|
||||
MockCValidationState state;
|
||||
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-valuebalance-toolarge", false)).Times(1);
|
||||
CheckTransactionWithoutProofVerification(tx, state);
|
||||
}
|
||||
|
||||
TEST(checktransaction_tests, ValueBalanceOverflowsTotal) {
|
||||
CMutableTransaction mtx = GetValidTransaction();
|
||||
mtx.vShieldedSpend.resize(1);
|
||||
mtx.vout[0].nValue = 1;
|
||||
mtx.valueBalance = -MAX_MONEY;
|
||||
|
||||
CTransaction tx(mtx);
|
||||
|
||||
MockCValidationState state;
|
||||
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge", false)).Times(1);
|
||||
CheckTransactionWithoutProofVerification(tx, state);
|
||||
}
|
||||
|
||||
TEST(checktransaction_tests, bad_txns_txouttotal_toolarge_joinsplit) {
|
||||
CMutableTransaction mtx = GetValidTransaction();
|
||||
mtx.vout[0].nValue = 1;
|
||||
@@ -361,7 +506,7 @@ TEST(checktransaction_tests, bad_txns_invalid_joinsplit_signature) {
|
||||
CTransaction tx(mtx);
|
||||
|
||||
MockCValidationState state;
|
||||
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false)).Times(1);
|
||||
EXPECT_CALL(state, DoS(0, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false)).Times(1);
|
||||
ContextualCheckTransaction(tx, state, 0, 100);
|
||||
}
|
||||
|
||||
@@ -394,14 +539,14 @@ TEST(checktransaction_tests, non_canonical_ed25519_signature) {
|
||||
CTransaction tx(mtx);
|
||||
|
||||
MockCValidationState state;
|
||||
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false)).Times(1);
|
||||
EXPECT_CALL(state, DoS(0, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false)).Times(1);
|
||||
ContextualCheckTransaction(tx, state, 0, 100);
|
||||
}
|
||||
|
||||
TEST(checktransaction_tests, OverwinterConstructors) {
|
||||
CMutableTransaction mtx;
|
||||
mtx.fOverwintered = true;
|
||||
mtx.nVersion = 3;
|
||||
mtx.nVersion = OVERWINTER_TX_VERSION;
|
||||
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
|
||||
mtx.nExpiryHeight = 20;
|
||||
|
||||
@@ -432,7 +577,7 @@ TEST(checktransaction_tests, OverwinterConstructors) {
|
||||
TEST(checktransaction_tests, OverwinterSerialization) {
|
||||
CMutableTransaction mtx;
|
||||
mtx.fOverwintered = true;
|
||||
mtx.nVersion = 3;
|
||||
mtx.nVersion = OVERWINTER_TX_VERSION;
|
||||
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
|
||||
mtx.nExpiryHeight = 99;
|
||||
|
||||
@@ -496,7 +641,7 @@ TEST(checktransaction_tests, OverwinterValidTx) {
|
||||
CMutableTransaction mtx = GetValidTransaction();
|
||||
mtx.vjoinsplit.resize(0);
|
||||
mtx.fOverwintered = true;
|
||||
mtx.nVersion = 3;
|
||||
mtx.nVersion = OVERWINTER_TX_VERSION;
|
||||
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
|
||||
mtx.nExpiryHeight = 0;
|
||||
CTransaction tx(mtx);
|
||||
@@ -508,7 +653,7 @@ TEST(checktransaction_tests, OverwinterExpiryHeight) {
|
||||
CMutableTransaction mtx = GetValidTransaction();
|
||||
mtx.vjoinsplit.resize(0);
|
||||
mtx.fOverwintered = true;
|
||||
mtx.nVersion = 3;
|
||||
mtx.nVersion = OVERWINTER_TX_VERSION;
|
||||
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
|
||||
mtx.nExpiryHeight = 0;
|
||||
|
||||
@@ -567,6 +712,58 @@ class UNSAFE_CTransaction : public CTransaction {
|
||||
UNSAFE_CTransaction(const CMutableTransaction &tx) : CTransaction(tx, true) {}
|
||||
};
|
||||
|
||||
TEST(checktransaction_tests, SaplingSproutInputSumsTooLarge) {
|
||||
CMutableTransaction mtx = GetValidTransaction();
|
||||
mtx.vjoinsplit.resize(0);
|
||||
mtx.fOverwintered = true;
|
||||
mtx.nVersion = SAPLING_TX_VERSION;
|
||||
mtx.nVersionGroupId = SAPLING_VERSION_GROUP_ID;
|
||||
mtx.nExpiryHeight = 0;
|
||||
|
||||
{
|
||||
// create JSDescription
|
||||
uint256 rt;
|
||||
uint256 joinSplitPubKey;
|
||||
std::array<libzcash::JSInput, ZC_NUM_JS_INPUTS> inputs = {
|
||||
libzcash::JSInput(),
|
||||
libzcash::JSInput()
|
||||
};
|
||||
std::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS> outputs = {
|
||||
libzcash::JSOutput(),
|
||||
libzcash::JSOutput()
|
||||
};
|
||||
std::array<size_t, ZC_NUM_JS_INPUTS> inputMap;
|
||||
std::array<size_t, ZC_NUM_JS_OUTPUTS> outputMap;
|
||||
|
||||
auto jsdesc = JSDescription::Randomized(
|
||||
true,
|
||||
*params, joinSplitPubKey, rt,
|
||||
inputs, outputs,
|
||||
inputMap, outputMap,
|
||||
0, 0, false);
|
||||
|
||||
mtx.vjoinsplit.push_back(jsdesc);
|
||||
}
|
||||
|
||||
mtx.vShieldedSpend.push_back(SpendDescription());
|
||||
|
||||
mtx.vjoinsplit[0].vpub_new = (MAX_MONEY / 2) + 10;
|
||||
|
||||
{
|
||||
UNSAFE_CTransaction tx(mtx);
|
||||
CValidationState state;
|
||||
EXPECT_TRUE(CheckTransactionWithoutProofVerification(tx, state));
|
||||
}
|
||||
|
||||
mtx.valueBalance = (MAX_MONEY / 2) + 10;
|
||||
|
||||
{
|
||||
UNSAFE_CTransaction tx(mtx);
|
||||
MockCValidationState state;
|
||||
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-txintotal-toolarge", false)).Times(1);
|
||||
CheckTransactionWithoutProofVerification(tx, state);
|
||||
}
|
||||
}
|
||||
|
||||
// Test bad Overwinter version number in CheckTransactionWithoutProofVerification
|
||||
TEST(checktransaction_tests, OverwinterVersionNumberLow) {
|
||||
@@ -610,7 +807,7 @@ TEST(checktransaction_tests, OverwinterBadVersionGroupId) {
|
||||
CMutableTransaction mtx = GetValidTransaction();
|
||||
mtx.vjoinsplit.resize(0);
|
||||
mtx.fOverwintered = true;
|
||||
mtx.nVersion = 3;
|
||||
mtx.nVersion = OVERWINTER_TX_VERSION;
|
||||
mtx.nExpiryHeight = 0;
|
||||
mtx.nVersionGroupId = 0x12345678;
|
||||
|
||||
@@ -626,13 +823,13 @@ TEST(checktransaction_tests, OverwinterNotActive) {
|
||||
|
||||
CMutableTransaction mtx = GetValidTransaction();
|
||||
mtx.fOverwintered = true;
|
||||
mtx.nVersion = 3;
|
||||
mtx.nVersion = OVERWINTER_TX_VERSION;
|
||||
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
|
||||
mtx.nExpiryHeight = 0;
|
||||
|
||||
CTransaction tx(mtx);
|
||||
MockCValidationState state;
|
||||
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "tx-overwinter-not-active", false)).Times(1);
|
||||
EXPECT_CALL(state, DoS(0, false, REJECT_INVALID, "tx-overwinter-not-active", false)).Times(1);
|
||||
ContextualCheckTransaction(tx, state, 1, 100);
|
||||
}
|
||||
|
||||
@@ -643,7 +840,7 @@ TEST(checktransaction_tests, OverwinterFlagNotSet) {
|
||||
|
||||
CMutableTransaction mtx = GetValidTransaction();
|
||||
mtx.fOverwintered = false;
|
||||
mtx.nVersion = 3;
|
||||
mtx.nVersion = OVERWINTER_TX_VERSION;
|
||||
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
|
||||
mtx.nExpiryHeight = 0;
|
||||
|
||||
@@ -684,7 +881,9 @@ TEST(checktransaction_tests, OverwinteredContextualCreateTx) {
|
||||
SelectParams(CBaseChainParams::REGTEST);
|
||||
const Consensus::Params& consensusParams = Params().GetConsensus();
|
||||
int activationHeight = 5;
|
||||
int saplingActivationHeight = 30;
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, activationHeight);
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, saplingActivationHeight);
|
||||
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(
|
||||
@@ -704,10 +903,64 @@ TEST(checktransaction_tests, OverwinteredContextualCreateTx) {
|
||||
EXPECT_EQ(mtx.nVersion, 3);
|
||||
EXPECT_EQ(mtx.fOverwintered, true);
|
||||
EXPECT_EQ(mtx.nVersionGroupId, OVERWINTER_VERSION_GROUP_ID);
|
||||
EXPECT_EQ(mtx.nExpiryHeight, 0);
|
||||
EXPECT_EQ(mtx.nExpiryHeight, activationHeight + expiryDelta);
|
||||
}
|
||||
|
||||
// Close to Sapling activation
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(
|
||||
consensusParams, saplingActivationHeight - expiryDelta - 2);
|
||||
|
||||
EXPECT_EQ(mtx.fOverwintered, true);
|
||||
EXPECT_EQ(mtx.nVersionGroupId, OVERWINTER_VERSION_GROUP_ID);
|
||||
EXPECT_EQ(mtx.nVersion, OVERWINTER_TX_VERSION);
|
||||
EXPECT_EQ(mtx.nExpiryHeight, saplingActivationHeight - 2);
|
||||
}
|
||||
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(
|
||||
consensusParams, saplingActivationHeight - expiryDelta - 1);
|
||||
|
||||
EXPECT_EQ(mtx.fOverwintered, true);
|
||||
EXPECT_EQ(mtx.nVersionGroupId, OVERWINTER_VERSION_GROUP_ID);
|
||||
EXPECT_EQ(mtx.nVersion, OVERWINTER_TX_VERSION);
|
||||
EXPECT_EQ(mtx.nExpiryHeight, saplingActivationHeight - 1);
|
||||
}
|
||||
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(
|
||||
consensusParams, saplingActivationHeight - expiryDelta);
|
||||
|
||||
EXPECT_EQ(mtx.fOverwintered, true);
|
||||
EXPECT_EQ(mtx.nVersionGroupId, OVERWINTER_VERSION_GROUP_ID);
|
||||
EXPECT_EQ(mtx.nVersion, OVERWINTER_TX_VERSION);
|
||||
EXPECT_EQ(mtx.nExpiryHeight, saplingActivationHeight - 1);
|
||||
}
|
||||
|
||||
// Just before Sapling activation
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(
|
||||
consensusParams, saplingActivationHeight - 1);
|
||||
|
||||
EXPECT_EQ(mtx.fOverwintered, true);
|
||||
EXPECT_EQ(mtx.nVersionGroupId, OVERWINTER_VERSION_GROUP_ID);
|
||||
EXPECT_EQ(mtx.nVersion, OVERWINTER_TX_VERSION);
|
||||
EXPECT_EQ(mtx.nExpiryHeight, saplingActivationHeight - 1);
|
||||
}
|
||||
|
||||
// Sapling activates
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(
|
||||
consensusParams, saplingActivationHeight);
|
||||
|
||||
EXPECT_EQ(mtx.fOverwintered, true);
|
||||
EXPECT_EQ(mtx.nVersionGroupId, SAPLING_VERSION_GROUP_ID);
|
||||
EXPECT_EQ(mtx.nVersion, SAPLING_TX_VERSION);
|
||||
EXPECT_EQ(mtx.nExpiryHeight, saplingActivationHeight + expiryDelta);
|
||||
}
|
||||
|
||||
// Revert to default
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
|
||||
}
|
||||
|
||||
@@ -771,4 +1024,4 @@ TEST(checktransaction_tests, BadTxReceivedOverNetwork)
|
||||
FAIL() << "Expected std::ios_base::failure 'Unknown transaction format', got some other exception";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ bool test_merkle_gadget(
|
||||
mgadget1.generate_r1cs_constraints();
|
||||
mgadget2.generate_r1cs_constraints();
|
||||
|
||||
ZCIncrementalMerkleTree tree;
|
||||
SproutMerkleTree tree;
|
||||
uint256 commitment1_data = uint256S("54d626e08c1c802b305dad30b7e54a82f102390cc92c7d4db112048935236e9c");
|
||||
uint256 commitment2_data = uint256S("59d2cde5e65c1414c32ba54f0fe4bdb3d67618125286e6a191317917c812c6d7");
|
||||
tree.append(commitment1_data);
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "chainparams.h"
|
||||
#include "clientversion.h"
|
||||
#include "deprecation.h"
|
||||
#include "init.h"
|
||||
#include "ui_interface.h"
|
||||
#include "util.h"
|
||||
#include "chainparams.h"
|
||||
#include "utilstrencodings.h"
|
||||
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#include <fstream>
|
||||
|
||||
using ::testing::StrictMock;
|
||||
|
||||
@@ -43,6 +47,18 @@ protected:
|
||||
}
|
||||
|
||||
StrictMock<MockUIInterface> mock_;
|
||||
|
||||
static std::vector<std::string> read_lines(boost::filesystem::path filepath) {
|
||||
std::vector<std::string> result;
|
||||
|
||||
std::ifstream f(filepath.string().c_str());
|
||||
std::string line;
|
||||
while (std::getline(f,line)) {
|
||||
result.push_back(line);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(DeprecationTest, NonDeprecatedNodeKeepsRunning) {
|
||||
@@ -91,22 +107,6 @@ TEST_F(DeprecationTest, DeprecatedNodeErrorIsRepeatedOnStartup) {
|
||||
EXPECT_TRUE(ShutdownRequested());
|
||||
}
|
||||
|
||||
TEST_F(DeprecationTest, DeprecatedNodeShutsDownIfOldVersionDisabled) {
|
||||
EXPECT_FALSE(ShutdownRequested());
|
||||
mapArgs["-disabledeprecation"] = "1.0.0";
|
||||
EXPECT_CALL(mock_, ThreadSafeMessageBox(::testing::_, "", CClientUIInterface::MSG_ERROR));
|
||||
EnforceNodeDeprecation(DEPRECATION_HEIGHT);
|
||||
EXPECT_TRUE(ShutdownRequested());
|
||||
}
|
||||
|
||||
TEST_F(DeprecationTest, DeprecatedNodeKeepsRunningIfCurrentVersionDisabled) {
|
||||
EXPECT_FALSE(ShutdownRequested());
|
||||
mapArgs["-disabledeprecation"] = CLIENT_VERSION_STR;
|
||||
EXPECT_CALL(mock_, ThreadSafeMessageBox(::testing::_, "", CClientUIInterface::MSG_ERROR));
|
||||
EnforceNodeDeprecation(DEPRECATION_HEIGHT);
|
||||
EXPECT_FALSE(ShutdownRequested());
|
||||
}
|
||||
|
||||
TEST_F(DeprecationTest, DeprecatedNodeIgnoredOnRegtest) {
|
||||
SelectParams(CBaseChainParams::REGTEST);
|
||||
EXPECT_FALSE(ShutdownRequested());
|
||||
@@ -119,4 +119,31 @@ TEST_F(DeprecationTest, DeprecatedNodeIgnoredOnTestnet) {
|
||||
EXPECT_FALSE(ShutdownRequested());
|
||||
EnforceNodeDeprecation(DEPRECATION_HEIGHT+1);
|
||||
EXPECT_FALSE(ShutdownRequested());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DeprecationTest, AlertNotify) {
|
||||
boost::filesystem::path temp = GetTempPath() /
|
||||
boost::filesystem::unique_path("alertnotify-%%%%.txt");
|
||||
|
||||
mapArgs["-alertnotify"] = std::string("echo %s >> ") + temp.string();
|
||||
|
||||
EXPECT_CALL(mock_, ThreadSafeMessageBox(::testing::_, "", CClientUIInterface::MSG_WARNING));
|
||||
EnforceNodeDeprecation(DEPRECATION_HEIGHT - DEPRECATION_WARN_LIMIT, false, false);
|
||||
|
||||
std::vector<std::string> r = read_lines(temp);
|
||||
EXPECT_EQ(r.size(), 1u);
|
||||
|
||||
// -alertnotify restricts the message to safe characters.
|
||||
auto expectedMsg = strprintf(
|
||||
"This version will be deprecated at block height %d, and will automatically shut down. You should upgrade to the latest version of Zcash.",
|
||||
DEPRECATION_HEIGHT);
|
||||
|
||||
// Windows built-in echo semantics are different than posixy shells. Quotes and
|
||||
// whitespace are printed literally.
|
||||
#ifndef WIN32
|
||||
EXPECT_EQ(r[0], expectedMsg);
|
||||
#else
|
||||
EXPECT_EQ(r[0], strprintf("'%s' ", expectedMsg));
|
||||
#endif
|
||||
boost::filesystem::remove(temp);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
//
|
||||
#if 0
|
||||
TEST(founders_reward_test, create_testnet_2of3multisig) {
|
||||
ECC_Start();
|
||||
SelectParams(CBaseChainParams::TESTNET);
|
||||
boost::filesystem::path pathTemp = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
|
||||
boost::filesystem::create_directories(pathTemp);
|
||||
@@ -60,7 +59,7 @@ TEST(founders_reward_test, create_testnet_2of3multisig) {
|
||||
pWallet->AddCScript(result);
|
||||
pWallet->SetAddressBook(innerID, "", "receive");
|
||||
|
||||
std::string address = CBitcoinAddress(innerID).ToString();
|
||||
std::string address = EncodeDestination(innerID);
|
||||
addresses.push_back(address);
|
||||
}
|
||||
|
||||
@@ -81,8 +80,6 @@ TEST(founders_reward_test, create_testnet_2of3multisig) {
|
||||
std::cout << s << std::endl;
|
||||
|
||||
pWallet->Flush(true);
|
||||
|
||||
ECC_Stop();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -107,11 +104,11 @@ TEST(founders_reward_test, general) {
|
||||
// address = t2ENg7hHVqqs9JwU5cgjvSbxnT2a9USNfhy
|
||||
// script.ToString() = OP_HASH160 55d64928e69829d9376c776550b6cc710d427153 OP_EQUAL
|
||||
// HexStr(script) = a91455d64928e69829d9376c776550b6cc710d42715387
|
||||
EXPECT_EQ(params.GetFoundersRewardScriptAtHeight(1), ParseHex("a914ef775f1f997f122a062fff1a2d7443abd1f9c64287"));
|
||||
EXPECT_EQ(HexStr(params.GetFoundersRewardScriptAtHeight(1)), "a914ef775f1f997f122a062fff1a2d7443abd1f9c64287");
|
||||
EXPECT_EQ(params.GetFoundersRewardAddressAtHeight(1), "t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi");
|
||||
EXPECT_EQ(params.GetFoundersRewardScriptAtHeight(53126), ParseHex("a914ac67f4c072668138d88a86ff21b27207b283212f87"));
|
||||
EXPECT_EQ(HexStr(params.GetFoundersRewardScriptAtHeight(53126)), "a914ac67f4c072668138d88a86ff21b27207b283212f87");
|
||||
EXPECT_EQ(params.GetFoundersRewardAddressAtHeight(53126), "t2NGQjYMQhFndDHguvUw4wZdNdsssA6K7x2");
|
||||
EXPECT_EQ(params.GetFoundersRewardScriptAtHeight(53127), ParseHex("a91455d64928e69829d9376c776550b6cc710d42715387"));
|
||||
EXPECT_EQ(HexStr(params.GetFoundersRewardScriptAtHeight(53127)), "a91455d64928e69829d9376c776550b6cc710d42715387");
|
||||
EXPECT_EQ(params.GetFoundersRewardAddressAtHeight(53127), "t2ENg7hHVqqs9JwU5cgjvSbxnT2a9USNfhy");
|
||||
|
||||
int maxHeight = params.GetConsensus().GetLastFoundersRewardBlockHeight();
|
||||
|
||||
@@ -3,192 +3,222 @@
|
||||
#include "utilstrencodings.h"
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/variant/get.hpp>
|
||||
|
||||
#include "zcash/prf.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "streams.h"
|
||||
#include "version.h"
|
||||
#include "serialize.h"
|
||||
#include "primitives/transaction.h"
|
||||
#include "zcash/JoinSplit.hpp"
|
||||
#include "zcash/Note.hpp"
|
||||
#include "zcash/NoteEncryption.hpp"
|
||||
#include "zcash/IncrementalMerkleTree.hpp"
|
||||
|
||||
#include <array>
|
||||
|
||||
using namespace libzcash;
|
||||
|
||||
extern ZCJoinSplit* params;
|
||||
|
||||
typedef std::array<JSDescription, 2> SproutProofs;
|
||||
// Make both the PHGR and Groth proof for a Sprout statement,
|
||||
// and store the results in JSDescription objects.
|
||||
SproutProofs makeSproutProofs(
|
||||
ZCJoinSplit& js,
|
||||
const std::array<JSInput, 2>& inputs,
|
||||
const std::array<JSOutput, 2>& outputs,
|
||||
const uint256& joinSplitPubKey,
|
||||
uint64_t vpub_old,
|
||||
uint64_t vpub_new,
|
||||
const uint256& rt
|
||||
){
|
||||
//Making the PHGR proof
|
||||
JSDescription phgr(false, js, joinSplitPubKey, rt, inputs, outputs, vpub_old, vpub_new);
|
||||
//Making the Groth proof
|
||||
JSDescription groth(true, js, joinSplitPubKey, rt, inputs, outputs, vpub_old, vpub_new);
|
||||
|
||||
return {phgr, groth};
|
||||
|
||||
}
|
||||
|
||||
bool verifySproutProofs(
|
||||
ZCJoinSplit& js,
|
||||
const SproutProofs& jsdescs,
|
||||
const uint256& joinSplitPubKey
|
||||
)
|
||||
{
|
||||
auto verifier = libzcash::ProofVerifier::Strict();
|
||||
bool phgrPassed = jsdescs[0].Verify(js, verifier, joinSplitPubKey);
|
||||
bool grothPassed = jsdescs[1].Verify(js, verifier, joinSplitPubKey);
|
||||
return phgrPassed && grothPassed;
|
||||
}
|
||||
|
||||
|
||||
void test_full_api(ZCJoinSplit* js)
|
||||
{
|
||||
// Create verification context.
|
||||
auto verifier = libzcash::ProofVerifier::Strict();
|
||||
|
||||
// The recipient's information.
|
||||
SpendingKey recipient_key = SpendingKey::random();
|
||||
PaymentAddress recipient_addr = recipient_key.address();
|
||||
SproutSpendingKey recipient_key = SproutSpendingKey::random();
|
||||
SproutPaymentAddress recipient_addr = recipient_key.address();
|
||||
|
||||
// Create the commitment tree
|
||||
ZCIncrementalMerkleTree tree;
|
||||
SproutMerkleTree 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 joinSplitPubKey = random_uint256();
|
||||
uint256 rt = tree.root();
|
||||
boost::array<ZCNoteEncryption::Ciphertext, 2> ciphertexts;
|
||||
ZCProof proof;
|
||||
SproutProofs jsdescs;
|
||||
|
||||
{
|
||||
boost::array<JSInput, 2> inputs = {
|
||||
std::array<JSInput, 2> inputs = {
|
||||
JSInput(), // dummy input
|
||||
JSInput() // dummy input
|
||||
};
|
||||
|
||||
boost::array<JSOutput, 2> outputs = {
|
||||
std::array<JSOutput, 2> outputs = {
|
||||
JSOutput(recipient_addr, 10),
|
||||
JSOutput() // dummy output
|
||||
};
|
||||
|
||||
boost::array<Note, 2> output_notes;
|
||||
std::array<SproutNote, 2> output_notes;
|
||||
|
||||
// Perform the proof
|
||||
proof = js->prove(
|
||||
// Perform the proofs
|
||||
jsdescs = makeSproutProofs(
|
||||
*js,
|
||||
inputs,
|
||||
outputs,
|
||||
output_notes,
|
||||
ciphertexts,
|
||||
ephemeralKey,
|
||||
pubKeyHash,
|
||||
randomSeed,
|
||||
macs,
|
||||
nullifiers,
|
||||
commitments,
|
||||
joinSplitPubKey,
|
||||
vpub_old,
|
||||
vpub_new,
|
||||
rt
|
||||
);
|
||||
}
|
||||
|
||||
// Verify the transaction:
|
||||
ASSERT_TRUE(js->verify(
|
||||
proof,
|
||||
verifier,
|
||||
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.receiving_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();
|
||||
// Verify both PHGR and Groth Proof:
|
||||
ASSERT_TRUE(verifySproutProofs(*js, jsdescs, joinSplitPubKey));
|
||||
|
||||
// Run tests using both phgr and groth as basis for field values
|
||||
for (auto jsdesc : jsdescs)
|
||||
{
|
||||
boost::array<JSInput, 2> inputs = {
|
||||
JSInput(), // dummy input
|
||||
JSInput(witness_recipient, decrypted_note, recipient_key)
|
||||
};
|
||||
SproutMerkleTree tree;
|
||||
SproutProofs jsdescs2;
|
||||
// Recipient should decrypt
|
||||
// Now the recipient should spend the money again
|
||||
auto h_sig = js->h_sig(jsdesc.randomSeed, jsdesc.nullifiers, joinSplitPubKey);
|
||||
ZCNoteDecryption decryptor(recipient_key.receiving_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
|
||||
auto note_pt = SproutNotePlaintext::decrypt(
|
||||
decryptor,
|
||||
jsdesc.ciphertexts[0],
|
||||
jsdesc.ephemeralKey,
|
||||
h_sig,
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
// Verify the transaction:
|
||||
ASSERT_TRUE(js->verify(
|
||||
proof,
|
||||
verifier,
|
||||
pubKeyHash,
|
||||
randomSeed,
|
||||
macs,
|
||||
nullifiers,
|
||||
commitments,
|
||||
vpub_old,
|
||||
vpub_new,
|
||||
rt
|
||||
));
|
||||
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(jsdesc.commitments[0]);
|
||||
auto witness_recipient = tree.witness();
|
||||
tree.append(jsdesc.commitments[1]);
|
||||
witness_recipient.append(jsdesc.commitments[1]);
|
||||
vpub_old = 0;
|
||||
vpub_new = 1;
|
||||
rt = tree.root();
|
||||
auto joinSplitPubKey2 = random_uint256();
|
||||
|
||||
{
|
||||
std::array<JSInput, 2> inputs = {
|
||||
JSInput(), // dummy input
|
||||
JSInput(witness_recipient, decrypted_note, recipient_key)
|
||||
};
|
||||
|
||||
SproutSpendingKey second_recipient = SproutSpendingKey::random();
|
||||
SproutPaymentAddress second_addr = second_recipient.address();
|
||||
|
||||
std::array<JSOutput, 2> outputs = {
|
||||
JSOutput(second_addr, 9),
|
||||
JSOutput() // dummy output
|
||||
};
|
||||
|
||||
std::array<SproutNote, 2> output_notes;
|
||||
|
||||
|
||||
// Perform the proofs
|
||||
jsdescs2 = makeSproutProofs(
|
||||
*js,
|
||||
inputs,
|
||||
outputs,
|
||||
joinSplitPubKey2,
|
||||
vpub_old,
|
||||
vpub_new,
|
||||
rt
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Verify both PHGR and Groth Proof:
|
||||
ASSERT_TRUE(verifySproutProofs(*js, jsdescs2, joinSplitPubKey2));
|
||||
}
|
||||
}
|
||||
|
||||
// Invokes the API (but does not compute a proof)
|
||||
// to test exceptions
|
||||
void invokeAPI(
|
||||
ZCJoinSplit* js,
|
||||
const boost::array<JSInput, 2>& inputs,
|
||||
const boost::array<JSOutput, 2>& outputs,
|
||||
const std::array<JSInput, 2>& inputs,
|
||||
const std::array<JSOutput, 2>& outputs,
|
||||
uint64_t vpub_old,
|
||||
uint64_t vpub_new,
|
||||
const uint256& rt
|
||||
) {
|
||||
uint256 ephemeralKey;
|
||||
uint256 randomSeed;
|
||||
uint256 pubKeyHash = random_uint256();
|
||||
boost::array<uint256, 2> macs;
|
||||
boost::array<uint256, 2> nullifiers;
|
||||
boost::array<uint256, 2> commitments;
|
||||
boost::array<ZCNoteEncryption::Ciphertext, 2> ciphertexts;
|
||||
uint256 joinSplitPubKey = random_uint256();
|
||||
std::array<uint256, 2> macs;
|
||||
std::array<uint256, 2> nullifiers;
|
||||
std::array<uint256, 2> commitments;
|
||||
std::array<ZCNoteEncryption::Ciphertext, 2> ciphertexts;
|
||||
|
||||
boost::array<Note, 2> output_notes;
|
||||
std::array<SproutNote, 2> output_notes;
|
||||
|
||||
ZCProof proof = js->prove(
|
||||
// PHGR
|
||||
SproutProof proof = js->prove(
|
||||
false,
|
||||
inputs,
|
||||
outputs,
|
||||
output_notes,
|
||||
ciphertexts,
|
||||
ephemeralKey,
|
||||
pubKeyHash,
|
||||
joinSplitPubKey,
|
||||
randomSeed,
|
||||
macs,
|
||||
nullifiers,
|
||||
commitments,
|
||||
vpub_old,
|
||||
vpub_new,
|
||||
rt,
|
||||
false
|
||||
);
|
||||
|
||||
// Groth
|
||||
proof = js->prove(
|
||||
true,
|
||||
inputs,
|
||||
outputs,
|
||||
output_notes,
|
||||
ciphertexts,
|
||||
ephemeralKey,
|
||||
joinSplitPubKey,
|
||||
randomSeed,
|
||||
macs,
|
||||
nullifiers,
|
||||
@@ -202,8 +232,8 @@ void invokeAPI(
|
||||
|
||||
void invokeAPIFailure(
|
||||
ZCJoinSplit* js,
|
||||
const boost::array<JSInput, 2>& inputs,
|
||||
const boost::array<JSOutput, 2>& outputs,
|
||||
const std::array<JSInput, 2>& inputs,
|
||||
const std::array<JSOutput, 2>& outputs,
|
||||
uint64_t vpub_old,
|
||||
uint64_t vpub_new,
|
||||
const uint256& rt,
|
||||
@@ -228,9 +258,9 @@ TEST(joinsplit, h_sig)
|
||||
import pyblake2
|
||||
import binascii
|
||||
|
||||
def hSig(randomSeed, nf1, nf2, pubKeyHash):
|
||||
def hSig(randomSeed, nf1, nf2, joinSplitPubKey):
|
||||
return pyblake2.blake2b(
|
||||
data=(randomSeed + nf1 + nf2 + pubKeyHash),
|
||||
data=(randomSeed + nf1 + nf2 + joinSplitPubKey),
|
||||
digest_size=32,
|
||||
person=b"ZcashComputehSig"
|
||||
).digest()
|
||||
@@ -297,12 +327,12 @@ for test_input in TEST_VECTORS:
|
||||
|
||||
void increment_note_witnesses(
|
||||
const uint256& element,
|
||||
std::vector<ZCIncrementalWitness>& witnesses,
|
||||
ZCIncrementalMerkleTree& tree
|
||||
std::vector<SproutWitness>& witnesses,
|
||||
SproutMerkleTree& tree
|
||||
)
|
||||
{
|
||||
tree.append(element);
|
||||
for (ZCIncrementalWitness& w : witnesses) {
|
||||
for (SproutWitness& w : witnesses) {
|
||||
w.append(element);
|
||||
}
|
||||
witnesses.push_back(tree.witness());
|
||||
@@ -311,20 +341,20 @@ void increment_note_witnesses(
|
||||
TEST(joinsplit, full_api_test)
|
||||
{
|
||||
{
|
||||
std::vector<ZCIncrementalWitness> witnesses;
|
||||
ZCIncrementalMerkleTree tree;
|
||||
std::vector<SproutWitness> witnesses;
|
||||
SproutMerkleTree tree;
|
||||
increment_note_witnesses(uint256(), witnesses, tree);
|
||||
SpendingKey sk = SpendingKey::random();
|
||||
PaymentAddress addr = sk.address();
|
||||
Note note1(addr.a_pk, 100, random_uint256(), random_uint256());
|
||||
SproutSpendingKey sk = SproutSpendingKey::random();
|
||||
SproutPaymentAddress addr = sk.address();
|
||||
SproutNote note1(addr.a_pk, 100, random_uint256(), random_uint256());
|
||||
increment_note_witnesses(note1.cm(), witnesses, tree);
|
||||
Note note2(addr.a_pk, 100, random_uint256(), random_uint256());
|
||||
SproutNote note2(addr.a_pk, 100, random_uint256(), random_uint256());
|
||||
increment_note_witnesses(note2.cm(), witnesses, tree);
|
||||
Note note3(addr.a_pk, 2100000000000001, random_uint256(), random_uint256());
|
||||
SproutNote note3(addr.a_pk, 2100000000000001, random_uint256(), random_uint256());
|
||||
increment_note_witnesses(note3.cm(), witnesses, tree);
|
||||
Note note4(addr.a_pk, 1900000000000000, random_uint256(), random_uint256());
|
||||
SproutNote note4(addr.a_pk, 1900000000000000, random_uint256(), random_uint256());
|
||||
increment_note_witnesses(note4.cm(), witnesses, tree);
|
||||
Note note5(addr.a_pk, 1900000000000000, random_uint256(), random_uint256());
|
||||
SproutNote note5(addr.a_pk, 1900000000000000, random_uint256(), random_uint256());
|
||||
increment_note_witnesses(note5.cm(), witnesses, tree);
|
||||
|
||||
// Should work
|
||||
@@ -419,7 +449,7 @@ TEST(joinsplit, full_api_test)
|
||||
// Wrong secret key
|
||||
invokeAPIFailure(params,
|
||||
{
|
||||
JSInput(witnesses[1], note1, SpendingKey::random()),
|
||||
JSInput(witnesses[1], note1, SproutSpendingKey::random()),
|
||||
JSInput()
|
||||
},
|
||||
{
|
||||
@@ -516,34 +546,71 @@ TEST(joinsplit, note_plaintexts)
|
||||
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);
|
||||
SproutPaymentAddress addr_pk(a_pk, pk_enc);
|
||||
|
||||
uint256 h_sig;
|
||||
|
||||
ZCNoteEncryption encryptor(h_sig);
|
||||
uint256 epk = encryptor.get_epk();
|
||||
|
||||
Note note(a_pk,
|
||||
SproutNote note(a_pk,
|
||||
1945813,
|
||||
random_uint256(),
|
||||
random_uint256()
|
||||
);
|
||||
|
||||
boost::array<unsigned char, ZC_MEMO_SIZE> memo;
|
||||
std::array<unsigned char, ZC_MEMO_SIZE> memo;
|
||||
|
||||
NotePlaintext note_pt(note, memo);
|
||||
SproutNotePlaintext 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 = SproutNotePlaintext::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_note.value() == note.value());
|
||||
|
||||
ASSERT_TRUE(decrypted.memo == note_pt.memo);
|
||||
ASSERT_TRUE(decrypted.memo() == note_pt.memo());
|
||||
|
||||
// Check memo() returns by reference, not return by value, for use cases such as:
|
||||
// std::string data(plaintext.memo().begin(), plaintext.memo().end());
|
||||
ASSERT_TRUE(decrypted.memo().data() == decrypted.memo().data());
|
||||
|
||||
// Check serialization of note plaintext
|
||||
CDataStream ss(SER_DISK, PROTOCOL_VERSION);
|
||||
ss << note_pt;
|
||||
SproutNotePlaintext note_pt2;
|
||||
ss >> note_pt2;
|
||||
ASSERT_EQ(note_pt.value(), note.value());
|
||||
ASSERT_EQ(note_pt.value(), note_pt2.value());
|
||||
ASSERT_EQ(note_pt.memo(), note_pt2.memo());
|
||||
ASSERT_EQ(note_pt.rho, note_pt2.rho);
|
||||
ASSERT_EQ(note_pt.r, note_pt2.r);
|
||||
}
|
||||
|
||||
TEST(joinsplit, note_class)
|
||||
{
|
||||
uint252 a_sk = uint252(uint256S("f6da8716682d600f74fc16bd0187faad6a26b4aa4c24d5c055b216d94516840e"));
|
||||
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);
|
||||
SproutPaymentAddress addr_pk(a_pk, pk_enc);
|
||||
|
||||
SproutNote note(a_pk,
|
||||
1945813,
|
||||
random_uint256(),
|
||||
random_uint256());
|
||||
|
||||
SproutNote clone = note;
|
||||
ASSERT_NE(¬e, &clone);
|
||||
ASSERT_EQ(note.value(), clone.value());
|
||||
ASSERT_EQ(note.cm(), clone.cm());
|
||||
ASSERT_EQ(note.rho, clone.rho);
|
||||
ASSERT_EQ(note.r, clone.r);
|
||||
ASSERT_EQ(note.a_pk, clone.a_pk);
|
||||
}
|
||||
|
||||
47
src/gtest/test_keys.cpp
Normal file
47
src/gtest/test_keys.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
#include <chainparams.h>
|
||||
#include <key_io.h>
|
||||
#include <zcash/Address.hpp>
|
||||
#include <zcash/zip32.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(Keys, DISABLED_EncodeAndDecodeSapling)
|
||||
{
|
||||
SelectParams(CBaseChainParams::MAIN);
|
||||
|
||||
std::vector<unsigned char, secure_allocator<unsigned char>> rawSeed(32);
|
||||
HDSeed seed(rawSeed);
|
||||
auto m = libzcash::SaplingExtendedSpendingKey::Master(seed);
|
||||
|
||||
for (uint32_t i = 0; i < 1000; i++) {
|
||||
auto sk = m.Derive(i);
|
||||
{
|
||||
std::string sk_string = EncodeSpendingKey(sk);
|
||||
EXPECT_EQ(
|
||||
sk_string.substr(0, 24),
|
||||
Params().Bech32HRP(CChainParams::SAPLING_EXTENDED_SPEND_KEY));
|
||||
|
||||
auto spendingkey2 = DecodeSpendingKey(sk_string);
|
||||
EXPECT_TRUE(IsValidSpendingKey(spendingkey2));
|
||||
|
||||
ASSERT_TRUE(boost::get<libzcash::SaplingExtendedSpendingKey>(&spendingkey2) != nullptr);
|
||||
auto sk2 = boost::get<libzcash::SaplingExtendedSpendingKey>(spendingkey2);
|
||||
EXPECT_EQ(sk, sk2);
|
||||
}
|
||||
{
|
||||
auto addr = sk.DefaultAddress();
|
||||
|
||||
std::string addr_string = EncodePaymentAddress(addr);
|
||||
EXPECT_EQ(
|
||||
addr_string.substr(0, 2),
|
||||
Params().Bech32HRP(CChainParams::SAPLING_PAYMENT_ADDRESS));
|
||||
|
||||
auto paymentaddr2 = DecodePaymentAddress(addr_string);
|
||||
EXPECT_TRUE(IsValidPaymentAddress(paymentaddr2));
|
||||
|
||||
ASSERT_TRUE(boost::get<libzcash::SaplingPaymentAddress>(&paymentaddr2) != nullptr);
|
||||
auto addr2 = boost::get<libzcash::SaplingPaymentAddress>(paymentaddr2);
|
||||
EXPECT_EQ(addr, addr2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +1,121 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "test/data/sapling_key_components.json.h"
|
||||
|
||||
#include "keystore.h"
|
||||
#include "random.h"
|
||||
#ifdef ENABLE_WALLET
|
||||
#include "wallet/crypter.h"
|
||||
#endif
|
||||
#include "zcash/Address.hpp"
|
||||
#include "zcash/zip32.h"
|
||||
|
||||
#include "json_test_vectors.h"
|
||||
|
||||
#define MAKE_STRING(x) std::string((x), (x)+sizeof(x))
|
||||
|
||||
TEST(keystore_tests, StoreAndRetrieveHDSeed) {
|
||||
CBasicKeyStore keyStore;
|
||||
HDSeed seedOut;
|
||||
|
||||
// When we haven't set a seed, we shouldn't get one
|
||||
EXPECT_FALSE(keyStore.HaveHDSeed());
|
||||
EXPECT_FALSE(keyStore.GetHDSeed(seedOut));
|
||||
|
||||
// Generate a random seed
|
||||
auto seed = HDSeed::Random();
|
||||
|
||||
// We should be able to set and retrieve the seed
|
||||
ASSERT_TRUE(keyStore.SetHDSeed(seed));
|
||||
EXPECT_TRUE(keyStore.HaveHDSeed());
|
||||
ASSERT_TRUE(keyStore.GetHDSeed(seedOut));
|
||||
EXPECT_EQ(seed, seedOut);
|
||||
|
||||
// Generate another random seed
|
||||
auto seed2 = HDSeed::Random();
|
||||
EXPECT_NE(seed, seed2);
|
||||
|
||||
// We should not be able to set and retrieve a different seed
|
||||
EXPECT_FALSE(keyStore.SetHDSeed(seed2));
|
||||
ASSERT_TRUE(keyStore.GetHDSeed(seedOut));
|
||||
EXPECT_EQ(seed, seedOut);
|
||||
}
|
||||
|
||||
TEST(keystore_tests, sapling_keys) {
|
||||
// ["sk, ask, nsk, ovk, ak, nk, ivk, default_d, default_pk_d, note_v, note_r, note_cm, note_pos, note_nf"],
|
||||
UniValue sapling_keys = read_json(MAKE_STRING(json_tests::sapling_key_components));
|
||||
|
||||
// Skipping over comments in sapling_key_components.json file
|
||||
for (size_t i = 2; i < 12; i++) {
|
||||
uint256 skSeed, ask, nsk, ovk, ak, nk, ivk;
|
||||
skSeed.SetHex(sapling_keys[i][0].getValStr());
|
||||
ask.SetHex(sapling_keys[i][1].getValStr());
|
||||
nsk.SetHex(sapling_keys[i][2].getValStr());
|
||||
ovk.SetHex(sapling_keys[i][3].getValStr());
|
||||
ak.SetHex(sapling_keys[i][4].getValStr());
|
||||
nk.SetHex(sapling_keys[i][5].getValStr());
|
||||
ivk.SetHex(sapling_keys[i][6].getValStr());
|
||||
|
||||
libzcash::diversifier_t default_d;
|
||||
std::copy_n(ParseHex(sapling_keys[i][7].getValStr()).begin(), 11, default_d.begin());
|
||||
|
||||
uint256 default_pk_d;
|
||||
default_pk_d.SetHex(sapling_keys[i][8].getValStr());
|
||||
|
||||
auto sk = libzcash::SaplingSpendingKey(skSeed);
|
||||
|
||||
// Check that expanded spending key from primitives and from sk are the same
|
||||
auto exp_sk_2 = libzcash::SaplingExpandedSpendingKey(ask, nsk, ovk);
|
||||
auto exp_sk = sk.expanded_spending_key();
|
||||
EXPECT_EQ(exp_sk, exp_sk_2);
|
||||
|
||||
// Check that full viewing key derived from sk and expanded sk are the same
|
||||
auto full_viewing_key = sk.full_viewing_key();
|
||||
EXPECT_EQ(full_viewing_key, exp_sk.full_viewing_key());
|
||||
|
||||
// Check that full viewing key from primitives and from sk are the same
|
||||
auto full_viewing_key_2 = libzcash::SaplingFullViewingKey(ak, nk, ovk);
|
||||
EXPECT_EQ(full_viewing_key, full_viewing_key_2);
|
||||
|
||||
// Check that incoming viewing key from primitives and from sk are the same
|
||||
auto in_viewing_key = full_viewing_key.in_viewing_key();
|
||||
auto in_viewing_key_2 = libzcash::SaplingIncomingViewingKey(ivk);
|
||||
EXPECT_EQ(in_viewing_key, in_viewing_key_2);
|
||||
|
||||
// Check that the default address from primitives and from sk method are the same
|
||||
auto default_addr = sk.default_address();
|
||||
auto addrOpt2 = in_viewing_key.address(default_d);
|
||||
EXPECT_TRUE(addrOpt2);
|
||||
auto default_addr_2 = addrOpt2.value();
|
||||
EXPECT_EQ(default_addr, default_addr_2);
|
||||
|
||||
auto default_addr_3 = libzcash::SaplingPaymentAddress(default_d, default_pk_d);
|
||||
EXPECT_EQ(default_addr_2, default_addr_3);
|
||||
EXPECT_EQ(default_addr, default_addr_3);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(keystore_tests, store_and_retrieve_spending_key) {
|
||||
CBasicKeyStore keyStore;
|
||||
libzcash::SpendingKey skOut;
|
||||
libzcash::SproutSpendingKey skOut;
|
||||
|
||||
std::set<libzcash::PaymentAddress> addrs;
|
||||
keyStore.GetPaymentAddresses(addrs);
|
||||
std::set<libzcash::SproutPaymentAddress> addrs;
|
||||
keyStore.GetSproutPaymentAddresses(addrs);
|
||||
EXPECT_EQ(0, addrs.size());
|
||||
|
||||
auto sk = libzcash::SpendingKey::random();
|
||||
auto sk = libzcash::SproutSpendingKey::random();
|
||||
auto addr = sk.address();
|
||||
|
||||
// Sanity-check: we can't get a key we haven't added
|
||||
EXPECT_FALSE(keyStore.HaveSpendingKey(addr));
|
||||
EXPECT_FALSE(keyStore.GetSpendingKey(addr, skOut));
|
||||
EXPECT_FALSE(keyStore.HaveSproutSpendingKey(addr));
|
||||
EXPECT_FALSE(keyStore.GetSproutSpendingKey(addr, skOut));
|
||||
|
||||
keyStore.AddSpendingKey(sk);
|
||||
EXPECT_TRUE(keyStore.HaveSpendingKey(addr));
|
||||
EXPECT_TRUE(keyStore.GetSpendingKey(addr, skOut));
|
||||
keyStore.AddSproutSpendingKey(sk);
|
||||
EXPECT_TRUE(keyStore.HaveSproutSpendingKey(addr));
|
||||
EXPECT_TRUE(keyStore.GetSproutSpendingKey(addr, skOut));
|
||||
EXPECT_EQ(sk, skOut);
|
||||
|
||||
keyStore.GetPaymentAddresses(addrs);
|
||||
keyStore.GetSproutPaymentAddresses(addrs);
|
||||
EXPECT_EQ(1, addrs.size());
|
||||
EXPECT_EQ(1, addrs.count(addr));
|
||||
}
|
||||
@@ -36,48 +124,48 @@ TEST(keystore_tests, store_and_retrieve_note_decryptor) {
|
||||
CBasicKeyStore keyStore;
|
||||
ZCNoteDecryption decOut;
|
||||
|
||||
auto sk = libzcash::SpendingKey::random();
|
||||
auto sk = libzcash::SproutSpendingKey::random();
|
||||
auto addr = sk.address();
|
||||
|
||||
EXPECT_FALSE(keyStore.GetNoteDecryptor(addr, decOut));
|
||||
|
||||
keyStore.AddSpendingKey(sk);
|
||||
keyStore.AddSproutSpendingKey(sk);
|
||||
EXPECT_TRUE(keyStore.GetNoteDecryptor(addr, decOut));
|
||||
EXPECT_EQ(ZCNoteDecryption(sk.receiving_key()), decOut);
|
||||
}
|
||||
|
||||
TEST(keystore_tests, StoreAndRetrieveViewingKey) {
|
||||
CBasicKeyStore keyStore;
|
||||
libzcash::ViewingKey vkOut;
|
||||
libzcash::SpendingKey skOut;
|
||||
libzcash::SproutViewingKey vkOut;
|
||||
libzcash::SproutSpendingKey skOut;
|
||||
ZCNoteDecryption decOut;
|
||||
|
||||
auto sk = libzcash::SpendingKey::random();
|
||||
auto sk = libzcash::SproutSpendingKey::random();
|
||||
auto vk = sk.viewing_key();
|
||||
auto addr = sk.address();
|
||||
|
||||
// Sanity-check: we can't get a viewing key we haven't added
|
||||
EXPECT_FALSE(keyStore.HaveViewingKey(addr));
|
||||
EXPECT_FALSE(keyStore.GetViewingKey(addr, vkOut));
|
||||
EXPECT_FALSE(keyStore.HaveSproutViewingKey(addr));
|
||||
EXPECT_FALSE(keyStore.GetSproutViewingKey(addr, vkOut));
|
||||
|
||||
// and we shouldn't have a spending key or decryptor either
|
||||
EXPECT_FALSE(keyStore.HaveSpendingKey(addr));
|
||||
EXPECT_FALSE(keyStore.GetSpendingKey(addr, skOut));
|
||||
EXPECT_FALSE(keyStore.HaveSproutSpendingKey(addr));
|
||||
EXPECT_FALSE(keyStore.GetSproutSpendingKey(addr, skOut));
|
||||
EXPECT_FALSE(keyStore.GetNoteDecryptor(addr, decOut));
|
||||
|
||||
// and we can't find it in our list of addresses
|
||||
std::set<libzcash::PaymentAddress> addresses;
|
||||
keyStore.GetPaymentAddresses(addresses);
|
||||
std::set<libzcash::SproutPaymentAddress> addresses;
|
||||
keyStore.GetSproutPaymentAddresses(addresses);
|
||||
EXPECT_FALSE(addresses.count(addr));
|
||||
|
||||
keyStore.AddViewingKey(vk);
|
||||
EXPECT_TRUE(keyStore.HaveViewingKey(addr));
|
||||
EXPECT_TRUE(keyStore.GetViewingKey(addr, vkOut));
|
||||
keyStore.AddSproutViewingKey(vk);
|
||||
EXPECT_TRUE(keyStore.HaveSproutViewingKey(addr));
|
||||
EXPECT_TRUE(keyStore.GetSproutViewingKey(addr, vkOut));
|
||||
EXPECT_EQ(vk, vkOut);
|
||||
|
||||
// We should still not have the spending key...
|
||||
EXPECT_FALSE(keyStore.HaveSpendingKey(addr));
|
||||
EXPECT_FALSE(keyStore.GetSpendingKey(addr, skOut));
|
||||
EXPECT_FALSE(keyStore.HaveSproutSpendingKey(addr));
|
||||
EXPECT_FALSE(keyStore.GetSproutSpendingKey(addr, skOut));
|
||||
|
||||
// ... but we should have a decryptor
|
||||
EXPECT_TRUE(keyStore.GetNoteDecryptor(addr, decOut));
|
||||
@@ -85,16 +173,16 @@ TEST(keystore_tests, StoreAndRetrieveViewingKey) {
|
||||
|
||||
// ... and we should find it in our list of addresses
|
||||
addresses.clear();
|
||||
keyStore.GetPaymentAddresses(addresses);
|
||||
keyStore.GetSproutPaymentAddresses(addresses);
|
||||
EXPECT_TRUE(addresses.count(addr));
|
||||
|
||||
keyStore.RemoveViewingKey(vk);
|
||||
EXPECT_FALSE(keyStore.HaveViewingKey(addr));
|
||||
EXPECT_FALSE(keyStore.GetViewingKey(addr, vkOut));
|
||||
EXPECT_FALSE(keyStore.HaveSpendingKey(addr));
|
||||
EXPECT_FALSE(keyStore.GetSpendingKey(addr, skOut));
|
||||
keyStore.RemoveSproutViewingKey(vk);
|
||||
EXPECT_FALSE(keyStore.HaveSproutViewingKey(addr));
|
||||
EXPECT_FALSE(keyStore.GetSproutViewingKey(addr, vkOut));
|
||||
EXPECT_FALSE(keyStore.HaveSproutSpendingKey(addr));
|
||||
EXPECT_FALSE(keyStore.GetSproutSpendingKey(addr, skOut));
|
||||
addresses.clear();
|
||||
keyStore.GetPaymentAddresses(addresses);
|
||||
keyStore.GetSproutPaymentAddresses(addresses);
|
||||
EXPECT_FALSE(addresses.count(addr));
|
||||
|
||||
// We still have a decryptor because those are cached in memory
|
||||
@@ -103,6 +191,49 @@ TEST(keystore_tests, StoreAndRetrieveViewingKey) {
|
||||
EXPECT_EQ(ZCNoteDecryption(sk.receiving_key()), decOut);
|
||||
}
|
||||
|
||||
// Sapling
|
||||
TEST(keystore_tests, StoreAndRetrieveSaplingSpendingKey) {
|
||||
CBasicKeyStore keyStore;
|
||||
libzcash::SaplingExtendedSpendingKey skOut;
|
||||
libzcash::SaplingFullViewingKey fvkOut;
|
||||
libzcash::SaplingIncomingViewingKey ivkOut;
|
||||
|
||||
std::vector<unsigned char, secure_allocator<unsigned char>> rawSeed(32);
|
||||
HDSeed seed(rawSeed);
|
||||
auto sk = libzcash::SaplingExtendedSpendingKey::Master(seed);
|
||||
auto fvk = sk.expsk.full_viewing_key();
|
||||
auto ivk = fvk.in_viewing_key();
|
||||
auto addr = sk.DefaultAddress();
|
||||
|
||||
// Sanity-check: we can't get a key we haven't added
|
||||
EXPECT_FALSE(keyStore.HaveSaplingSpendingKey(fvk));
|
||||
EXPECT_FALSE(keyStore.GetSaplingSpendingKey(fvk, skOut));
|
||||
// Sanity-check: we can't get a full viewing key we haven't added
|
||||
EXPECT_FALSE(keyStore.HaveSaplingFullViewingKey(ivk));
|
||||
EXPECT_FALSE(keyStore.GetSaplingFullViewingKey(ivk, fvkOut));
|
||||
// Sanity-check: we can't get an incoming viewing key we haven't added
|
||||
EXPECT_FALSE(keyStore.HaveSaplingIncomingViewingKey(addr));
|
||||
EXPECT_FALSE(keyStore.GetSaplingIncomingViewingKey(addr, ivkOut));
|
||||
|
||||
// If we don't specify the default address, that mapping isn't created
|
||||
keyStore.AddSaplingSpendingKey(sk);
|
||||
EXPECT_TRUE(keyStore.HaveSaplingSpendingKey(fvk));
|
||||
EXPECT_TRUE(keyStore.HaveSaplingFullViewingKey(ivk));
|
||||
EXPECT_FALSE(keyStore.HaveSaplingIncomingViewingKey(addr));
|
||||
|
||||
// When we specify the default address, we get the full mapping
|
||||
keyStore.AddSaplingSpendingKey(sk, addr);
|
||||
EXPECT_TRUE(keyStore.HaveSaplingSpendingKey(fvk));
|
||||
EXPECT_TRUE(keyStore.GetSaplingSpendingKey(fvk, skOut));
|
||||
EXPECT_TRUE(keyStore.HaveSaplingFullViewingKey(ivk));
|
||||
EXPECT_TRUE(keyStore.GetSaplingFullViewingKey(ivk, fvkOut));
|
||||
EXPECT_TRUE(keyStore.HaveSaplingIncomingViewingKey(addr));
|
||||
EXPECT_TRUE(keyStore.GetSaplingIncomingViewingKey(addr, ivkOut));
|
||||
EXPECT_EQ(sk, skOut);
|
||||
EXPECT_EQ(fvk, fvkOut);
|
||||
EXPECT_EQ(ivk, ivkOut);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_WALLET
|
||||
class TestCCryptoKeyStore : public CCryptoKeyStore
|
||||
{
|
||||
@@ -111,29 +242,89 @@ public:
|
||||
bool Unlock(const CKeyingMaterial& vMasterKeyIn) { return CCryptoKeyStore::Unlock(vMasterKeyIn); }
|
||||
};
|
||||
|
||||
TEST(keystore_tests, StoreAndRetrieveHDSeedInEncryptedStore) {
|
||||
TestCCryptoKeyStore keyStore;
|
||||
CKeyingMaterial vMasterKey(32, 0);
|
||||
GetRandBytes(vMasterKey.data(), 32);
|
||||
HDSeed seedOut;
|
||||
|
||||
// 1) Test adding a seed to an unencrypted key store, then encrypting it
|
||||
auto seed = HDSeed::Random();
|
||||
EXPECT_FALSE(keyStore.HaveHDSeed());
|
||||
EXPECT_FALSE(keyStore.GetHDSeed(seedOut));
|
||||
|
||||
ASSERT_TRUE(keyStore.SetHDSeed(seed));
|
||||
EXPECT_TRUE(keyStore.HaveHDSeed());
|
||||
ASSERT_TRUE(keyStore.GetHDSeed(seedOut));
|
||||
EXPECT_EQ(seed, seedOut);
|
||||
|
||||
ASSERT_TRUE(keyStore.EncryptKeys(vMasterKey));
|
||||
EXPECT_FALSE(keyStore.GetHDSeed(seedOut));
|
||||
|
||||
// Unlocking with a random key should fail
|
||||
CKeyingMaterial vRandomKey(32, 0);
|
||||
GetRandBytes(vRandomKey.data(), 32);
|
||||
EXPECT_FALSE(keyStore.Unlock(vRandomKey));
|
||||
|
||||
// Unlocking with a slightly-modified vMasterKey should fail
|
||||
CKeyingMaterial vModifiedKey(vMasterKey);
|
||||
vModifiedKey[0] += 1;
|
||||
EXPECT_FALSE(keyStore.Unlock(vModifiedKey));
|
||||
|
||||
// Unlocking with vMasterKey should succeed
|
||||
ASSERT_TRUE(keyStore.Unlock(vMasterKey));
|
||||
ASSERT_TRUE(keyStore.GetHDSeed(seedOut));
|
||||
EXPECT_EQ(seed, seedOut);
|
||||
|
||||
// 2) Test replacing the seed in an already-encrypted key store fails
|
||||
auto seed2 = HDSeed::Random();
|
||||
EXPECT_FALSE(keyStore.SetHDSeed(seed2));
|
||||
EXPECT_TRUE(keyStore.HaveHDSeed());
|
||||
ASSERT_TRUE(keyStore.GetHDSeed(seedOut));
|
||||
EXPECT_EQ(seed, seedOut);
|
||||
|
||||
// 3) Test adding a new seed to an already-encrypted key store
|
||||
TestCCryptoKeyStore keyStore2;
|
||||
|
||||
// Add a Sprout address so the wallet has something to test when decrypting
|
||||
ASSERT_TRUE(keyStore2.AddSproutSpendingKey(libzcash::SproutSpendingKey::random()));
|
||||
|
||||
ASSERT_TRUE(keyStore2.EncryptKeys(vMasterKey));
|
||||
ASSERT_TRUE(keyStore2.Unlock(vMasterKey));
|
||||
|
||||
EXPECT_FALSE(keyStore2.HaveHDSeed());
|
||||
EXPECT_FALSE(keyStore2.GetHDSeed(seedOut));
|
||||
|
||||
auto seed3 = HDSeed::Random();
|
||||
ASSERT_TRUE(keyStore2.SetHDSeed(seed3));
|
||||
EXPECT_TRUE(keyStore2.HaveHDSeed());
|
||||
ASSERT_TRUE(keyStore2.GetHDSeed(seedOut));
|
||||
EXPECT_EQ(seed3, seedOut);
|
||||
}
|
||||
|
||||
TEST(keystore_tests, store_and_retrieve_spending_key_in_encrypted_store) {
|
||||
TestCCryptoKeyStore keyStore;
|
||||
uint256 r {GetRandHash()};
|
||||
CKeyingMaterial vMasterKey (r.begin(), r.end());
|
||||
libzcash::SpendingKey keyOut;
|
||||
libzcash::SproutSpendingKey keyOut;
|
||||
ZCNoteDecryption decOut;
|
||||
std::set<libzcash::PaymentAddress> addrs;
|
||||
std::set<libzcash::SproutPaymentAddress> addrs;
|
||||
|
||||
// 1) Test adding a key to an unencrypted key store, then encrypting it
|
||||
auto sk = libzcash::SpendingKey::random();
|
||||
auto sk = libzcash::SproutSpendingKey::random();
|
||||
auto addr = sk.address();
|
||||
EXPECT_FALSE(keyStore.GetNoteDecryptor(addr, decOut));
|
||||
|
||||
keyStore.AddSpendingKey(sk);
|
||||
ASSERT_TRUE(keyStore.HaveSpendingKey(addr));
|
||||
ASSERT_TRUE(keyStore.GetSpendingKey(addr, keyOut));
|
||||
keyStore.AddSproutSpendingKey(sk);
|
||||
ASSERT_TRUE(keyStore.HaveSproutSpendingKey(addr));
|
||||
ASSERT_TRUE(keyStore.GetSproutSpendingKey(addr, keyOut));
|
||||
ASSERT_EQ(sk, keyOut);
|
||||
EXPECT_TRUE(keyStore.GetNoteDecryptor(addr, decOut));
|
||||
EXPECT_EQ(ZCNoteDecryption(sk.receiving_key()), decOut);
|
||||
|
||||
ASSERT_TRUE(keyStore.EncryptKeys(vMasterKey));
|
||||
ASSERT_TRUE(keyStore.HaveSpendingKey(addr));
|
||||
ASSERT_FALSE(keyStore.GetSpendingKey(addr, keyOut));
|
||||
ASSERT_TRUE(keyStore.HaveSproutSpendingKey(addr));
|
||||
ASSERT_FALSE(keyStore.GetSproutSpendingKey(addr, keyOut));
|
||||
EXPECT_TRUE(keyStore.GetNoteDecryptor(addr, decOut));
|
||||
EXPECT_EQ(ZCNoteDecryption(sk.receiving_key()), decOut);
|
||||
|
||||
@@ -149,38 +340,38 @@ TEST(keystore_tests, store_and_retrieve_spending_key_in_encrypted_store) {
|
||||
|
||||
// Unlocking with vMasterKey should succeed
|
||||
ASSERT_TRUE(keyStore.Unlock(vMasterKey));
|
||||
ASSERT_TRUE(keyStore.GetSpendingKey(addr, keyOut));
|
||||
ASSERT_TRUE(keyStore.GetSproutSpendingKey(addr, keyOut));
|
||||
ASSERT_EQ(sk, keyOut);
|
||||
|
||||
keyStore.GetPaymentAddresses(addrs);
|
||||
keyStore.GetSproutPaymentAddresses(addrs);
|
||||
ASSERT_EQ(1, addrs.size());
|
||||
ASSERT_EQ(1, addrs.count(addr));
|
||||
|
||||
// 2) Test adding a spending key to an already-encrypted key store
|
||||
auto sk2 = libzcash::SpendingKey::random();
|
||||
auto sk2 = libzcash::SproutSpendingKey::random();
|
||||
auto addr2 = sk2.address();
|
||||
EXPECT_FALSE(keyStore.GetNoteDecryptor(addr2, decOut));
|
||||
|
||||
keyStore.AddSpendingKey(sk2);
|
||||
ASSERT_TRUE(keyStore.HaveSpendingKey(addr2));
|
||||
ASSERT_TRUE(keyStore.GetSpendingKey(addr2, keyOut));
|
||||
keyStore.AddSproutSpendingKey(sk2);
|
||||
ASSERT_TRUE(keyStore.HaveSproutSpendingKey(addr2));
|
||||
ASSERT_TRUE(keyStore.GetSproutSpendingKey(addr2, keyOut));
|
||||
ASSERT_EQ(sk2, keyOut);
|
||||
EXPECT_TRUE(keyStore.GetNoteDecryptor(addr2, decOut));
|
||||
EXPECT_EQ(ZCNoteDecryption(sk2.receiving_key()), decOut);
|
||||
|
||||
ASSERT_TRUE(keyStore.Lock());
|
||||
ASSERT_TRUE(keyStore.HaveSpendingKey(addr2));
|
||||
ASSERT_FALSE(keyStore.GetSpendingKey(addr2, keyOut));
|
||||
ASSERT_TRUE(keyStore.HaveSproutSpendingKey(addr2));
|
||||
ASSERT_FALSE(keyStore.GetSproutSpendingKey(addr2, keyOut));
|
||||
EXPECT_TRUE(keyStore.GetNoteDecryptor(addr2, decOut));
|
||||
EXPECT_EQ(ZCNoteDecryption(sk2.receiving_key()), decOut);
|
||||
|
||||
ASSERT_TRUE(keyStore.Unlock(vMasterKey));
|
||||
ASSERT_TRUE(keyStore.GetSpendingKey(addr2, keyOut));
|
||||
ASSERT_TRUE(keyStore.GetSproutSpendingKey(addr2, keyOut));
|
||||
ASSERT_EQ(sk2, keyOut);
|
||||
EXPECT_TRUE(keyStore.GetNoteDecryptor(addr2, decOut));
|
||||
EXPECT_EQ(ZCNoteDecryption(sk2.receiving_key()), decOut);
|
||||
|
||||
keyStore.GetPaymentAddresses(addrs);
|
||||
keyStore.GetSproutPaymentAddresses(addrs);
|
||||
ASSERT_EQ(2, addrs.size());
|
||||
ASSERT_EQ(1, addrs.count(addr));
|
||||
ASSERT_EQ(1, addrs.count(addr2));
|
||||
|
||||
@@ -19,11 +19,15 @@ class FakeCoinsViewDB : public CCoinsView {
|
||||
public:
|
||||
FakeCoinsViewDB() {}
|
||||
|
||||
bool GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const {
|
||||
bool GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetNullifier(const uint256 &nf) const {
|
||||
bool GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetNullifier(const uint256 &nf, ShieldedType type) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -47,16 +51,19 @@ public:
|
||||
return a;
|
||||
}
|
||||
|
||||
uint256 GetBestAnchor() const {
|
||||
uint256 GetBestAnchor(ShieldedType type) const {
|
||||
uint256 a;
|
||||
return a;
|
||||
}
|
||||
|
||||
bool BatchWrite(CCoinsMap &mapCoins,
|
||||
const uint256 &hashBlock,
|
||||
const uint256 &hashAnchor,
|
||||
CAnchorsMap &mapAnchors,
|
||||
CNullifiersMap &mapNullifiers) {
|
||||
const uint256 &hashSproutAnchor,
|
||||
const uint256 &hashSaplingAnchor,
|
||||
CAnchorsSproutMap &mapSproutAnchors,
|
||||
CAnchorsSaplingMap &mapSaplingAnchors,
|
||||
CNullifiersMap &mapSproutNullifiers,
|
||||
CNullifiersMap &mapSaplingNullifiers) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -96,7 +103,7 @@ TEST(Mempool, PriorityStatsDoNotCrash) {
|
||||
}
|
||||
|
||||
TEST(Mempool, TxInputLimit) {
|
||||
SelectParams(CBaseChainParams::TESTNET);
|
||||
SelectParams(CBaseChainParams::REGTEST);
|
||||
|
||||
CTxMemPool pool(::minRelayTxFee);
|
||||
bool missingInputs;
|
||||
@@ -133,13 +140,30 @@ TEST(Mempool, TxInputLimit) {
|
||||
// The -mempooltxinputlimit check doesn't set a reason
|
||||
EXPECT_EQ(state3.GetRejectReason(), "");
|
||||
|
||||
// Clear the limit
|
||||
mapArgs.erase("-mempooltxinputlimit");
|
||||
// Activate Overwinter
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
|
||||
|
||||
// Check it no longer fails due to exceeding the limit
|
||||
CValidationState state4;
|
||||
EXPECT_FALSE(AcceptToMemoryPool(pool, state4, tx3, false, &missingInputs));
|
||||
EXPECT_EQ(state4.GetRejectReason(), "bad-txns-version-too-low");
|
||||
|
||||
// Deactivate Overwinter
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
|
||||
|
||||
// Check it now fails due to exceeding the limit
|
||||
CValidationState state5;
|
||||
EXPECT_FALSE(AcceptToMemoryPool(pool, state5, tx3, false, &missingInputs));
|
||||
// The -mempooltxinputlimit check doesn't set a reason
|
||||
EXPECT_EQ(state5.GetRejectReason(), "");
|
||||
|
||||
// Clear the limit
|
||||
mapArgs.erase("-mempooltxinputlimit");
|
||||
|
||||
// Check it no longer fails due to exceeding the limit
|
||||
CValidationState state6;
|
||||
EXPECT_FALSE(AcceptToMemoryPool(pool, state6, tx3, false, &missingInputs));
|
||||
EXPECT_EQ(state6.GetRejectReason(), "bad-txns-version-too-low");
|
||||
}
|
||||
|
||||
// Valid overwinter v3 format tx gets rejected because overwinter hasn't activated yet.
|
||||
@@ -152,7 +176,7 @@ TEST(Mempool, OverwinterNotActiveYet) {
|
||||
CMutableTransaction mtx = GetValidTransaction();
|
||||
mtx.vjoinsplit.resize(0); // no joinsplits
|
||||
mtx.fOverwintered = true;
|
||||
mtx.nVersion = 3;
|
||||
mtx.nVersion = OVERWINTER_TX_VERSION;
|
||||
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
|
||||
mtx.nExpiryHeight = 0;
|
||||
CValidationState state1;
|
||||
@@ -224,7 +248,7 @@ TEST(Mempool, SproutNegativeVersionTxWhenOverwinterActive) {
|
||||
mtx.vjoinsplit.resize(0); // no joinsplits
|
||||
mtx.fOverwintered = false;
|
||||
|
||||
// A Sprout transaction with version -3 is created using Sprout code (as found in Zcashd <= 1.0.14).
|
||||
// A Sprout transaction with version -3 is created using Sprout code (as found in zcashd <= 1.0.14).
|
||||
// First four bytes of transaction, parsed as an uint32_t, has the value: 0xfffffffd
|
||||
// This test simulates an Overwinter node receiving this transaction, but incorrectly deserializing the
|
||||
// transaction due to a (pretend) bug of not detecting the most significant bit, which leads
|
||||
@@ -242,7 +266,7 @@ TEST(Mempool, SproutNegativeVersionTxWhenOverwinterActive) {
|
||||
EXPECT_EQ(state1.GetRejectReason(), "bad-txns-version-too-low");
|
||||
}
|
||||
|
||||
// A Sprout transaction with version -3 created using Overwinter code (as found in Zcashd >= 1.0.15).
|
||||
// A Sprout transaction with version -3 created using Overwinter code (as found in zcashd >= 1.0.15).
|
||||
// First four bytes of transaction, parsed as an uint32_t, has the value: 0x80000003
|
||||
// This test simulates the same pretend bug described above.
|
||||
// The resulting Sprout tx with nVersion -2147483645 should be rejected by the Overwinter node's mempool.
|
||||
|
||||
@@ -7,6 +7,13 @@
|
||||
#include "test/data/merkle_path.json.h"
|
||||
#include "test/data/merkle_commitments.json.h"
|
||||
|
||||
#include "test/data/merkle_roots_sapling.json.h"
|
||||
#include "test/data/merkle_roots_empty_sapling.json.h"
|
||||
#include "test/data/merkle_serialization_sapling.json.h"
|
||||
#include "test/data/merkle_witness_serialization_sapling.json.h"
|
||||
#include "test/data/merkle_path_sapling.json.h"
|
||||
#include "test/data/merkle_commitments_sapling.json.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <stdexcept>
|
||||
@@ -32,7 +39,7 @@ using namespace std;
|
||||
using namespace libsnark;
|
||||
|
||||
template<>
|
||||
void expect_deser_same(const ZCTestingIncrementalWitness& expected)
|
||||
void expect_deser_same(const SproutTestingWitness& expected)
|
||||
{
|
||||
// Cannot check this; IncrementalWitness cannot be
|
||||
// deserialized because it can only be constructed by
|
||||
@@ -40,16 +47,6 @@ void expect_deser_same(const ZCTestingIncrementalWitness& expected)
|
||||
// canonical serialized representation.
|
||||
}
|
||||
|
||||
template<>
|
||||
void expect_deser_same(const libzcash::MerklePath& expected)
|
||||
{
|
||||
// This deserialization check is pointless for MerklePath,
|
||||
// since we only serialize it to check it against test
|
||||
// vectors. See `expect_test_vector` for that. Also,
|
||||
// it doesn't seem that vector<bool> can be properly
|
||||
// deserialized by Bitcoin's serialization code.
|
||||
}
|
||||
|
||||
template<typename A, typename B, typename C>
|
||||
void expect_ser_test_vector(B& b, const C& c, const A& tree) {
|
||||
expect_test_vector<B, C>(b, c);
|
||||
@@ -61,7 +58,8 @@ void test_tree(
|
||||
UniValue root_tests,
|
||||
UniValue ser_tests,
|
||||
UniValue witness_ser_tests,
|
||||
UniValue path_tests
|
||||
UniValue path_tests,
|
||||
bool libsnark_test
|
||||
)
|
||||
{
|
||||
size_t witness_ser_i = 0;
|
||||
@@ -116,10 +114,9 @@ void test_tree(
|
||||
ASSERT_THROW(wit.element(), std::runtime_error);
|
||||
} else {
|
||||
auto path = wit.path();
|
||||
expect_test_vector(path_tests[path_i++], path);
|
||||
|
||||
{
|
||||
expect_test_vector(path_tests[path_i++], path);
|
||||
|
||||
if (libsnark_test) {
|
||||
typedef Fr<default_r1cs_ppzksnark_pp> FieldT;
|
||||
|
||||
protoboard<FieldT> pb;
|
||||
@@ -146,7 +143,7 @@ void test_tree(
|
||||
size_t path_index = convertVectorToInt(path.index);
|
||||
|
||||
commitment.bits.fill_with_bits(pb, bit_vector(commitment_bv));
|
||||
positions.fill_with_bits_of_ulong(pb, path_index);
|
||||
positions.fill_with_bits_of_uint64(pb, path_index);
|
||||
|
||||
authvars.generate_r1cs_witness(path_index, path.authentication_path);
|
||||
auth.generate_r1cs_witness();
|
||||
@@ -198,7 +195,31 @@ TEST(merkletree, vectors) {
|
||||
UniValue path_tests = read_json(MAKE_STRING(json_tests::merkle_path));
|
||||
UniValue commitment_tests = read_json(MAKE_STRING(json_tests::merkle_commitments));
|
||||
|
||||
test_tree<ZCTestingIncrementalMerkleTree, ZCTestingIncrementalWitness>(commitment_tests, root_tests, ser_tests, witness_ser_tests, path_tests);
|
||||
test_tree<SproutTestingMerkleTree, SproutTestingWitness>(
|
||||
commitment_tests,
|
||||
root_tests,
|
||||
ser_tests,
|
||||
witness_ser_tests,
|
||||
path_tests,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
TEST(merkletree, SaplingVectors) {
|
||||
UniValue root_tests = read_json(MAKE_STRING(json_tests::merkle_roots_sapling));
|
||||
UniValue ser_tests = read_json(MAKE_STRING(json_tests::merkle_serialization_sapling));
|
||||
UniValue witness_ser_tests = read_json(MAKE_STRING(json_tests::merkle_witness_serialization_sapling));
|
||||
UniValue path_tests = read_json(MAKE_STRING(json_tests::merkle_path_sapling));
|
||||
UniValue commitment_tests = read_json(MAKE_STRING(json_tests::merkle_commitments_sapling));
|
||||
|
||||
test_tree<SaplingTestingMerkleTree, SaplingTestingWitness>(
|
||||
commitment_tests,
|
||||
root_tests,
|
||||
ser_tests,
|
||||
witness_ser_tests,
|
||||
path_tests,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
TEST(merkletree, emptyroots) {
|
||||
@@ -214,19 +235,41 @@ TEST(merkletree, emptyroots) {
|
||||
ASSERT_TRUE(INCREMENTAL_MERKLE_TREE_DEPTH <= 64);
|
||||
}
|
||||
|
||||
TEST(merkletree, EmptyrootsSapling) {
|
||||
UniValue empty_roots = read_json(MAKE_STRING(json_tests::merkle_roots_empty_sapling));
|
||||
|
||||
libzcash::EmptyMerkleRoots<62, libzcash::PedersenHash> emptyroots;
|
||||
|
||||
for (size_t depth = 0; depth <= 62; depth++) {
|
||||
expect_test_vector(empty_roots[depth], emptyroots.empty_root(depth));
|
||||
}
|
||||
|
||||
// Double check that we're testing (at least) all the empty roots we'll use.
|
||||
ASSERT_TRUE(INCREMENTAL_MERKLE_TREE_DEPTH <= 62);
|
||||
}
|
||||
|
||||
TEST(merkletree, emptyroot) {
|
||||
// This literal is the depth-20 empty tree root with the bytes reversed to
|
||||
// account for the fact that uint256S() loads a big-endian representation of
|
||||
// an integer which converted to little-endian internally.
|
||||
uint256 expected = uint256S("59d2cde5e65c1414c32ba54f0fe4bdb3d67618125286e6a191317917c812c6d7");
|
||||
|
||||
ASSERT_TRUE(ZCIncrementalMerkleTree::empty_root() == expected);
|
||||
ASSERT_TRUE(SproutMerkleTree::empty_root() == expected);
|
||||
}
|
||||
|
||||
TEST(merkletree, EmptyrootSapling) {
|
||||
// This literal is the depth-20 empty tree root with the bytes reversed to
|
||||
// account for the fact that uint256S() loads a big-endian representation of
|
||||
// an integer which converted to little-endian internally.
|
||||
uint256 expected = uint256S("3e49b5f954aa9d3545bc6c37744661eea48d7c34e3000d82b7f0010c30f4c2fb");
|
||||
|
||||
ASSERT_TRUE(SaplingMerkleTree::empty_root() == expected);
|
||||
}
|
||||
|
||||
TEST(merkletree, deserializeInvalid) {
|
||||
// attempt to deserialize a small tree from a serialized large tree
|
||||
// (exceeds depth well-formedness check)
|
||||
ZCIncrementalMerkleTree newTree;
|
||||
SproutMerkleTree newTree;
|
||||
|
||||
for (size_t i = 0; i < 16; i++) {
|
||||
newTree.append(uint256S("54d626e08c1c802b305dad30b7e54a82f102390cc92c7d4db112048935236e9c"));
|
||||
@@ -237,7 +280,7 @@ TEST(merkletree, deserializeInvalid) {
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << newTree;
|
||||
|
||||
ZCTestingIncrementalMerkleTree newTreeSmall;
|
||||
SproutTestingMerkleTree newTreeSmall;
|
||||
ASSERT_THROW({ss >> newTreeSmall;}, std::ios_base::failure);
|
||||
}
|
||||
|
||||
@@ -249,7 +292,7 @@ TEST(merkletree, deserializeInvalid2) {
|
||||
PROTOCOL_VERSION
|
||||
);
|
||||
|
||||
ZCIncrementalMerkleTree tree;
|
||||
SproutMerkleTree tree;
|
||||
ASSERT_THROW(ss >> tree, std::ios_base::failure);
|
||||
}
|
||||
|
||||
@@ -261,7 +304,7 @@ TEST(merkletree, deserializeInvalid3) {
|
||||
PROTOCOL_VERSION
|
||||
);
|
||||
|
||||
ZCIncrementalMerkleTree tree;
|
||||
SproutMerkleTree tree;
|
||||
ASSERT_THROW(ss >> tree, std::ios_base::failure);
|
||||
}
|
||||
|
||||
@@ -273,15 +316,15 @@ TEST(merkletree, deserializeInvalid4) {
|
||||
PROTOCOL_VERSION
|
||||
);
|
||||
|
||||
ZCIncrementalMerkleTree tree;
|
||||
SproutMerkleTree tree;
|
||||
ASSERT_THROW(ss >> tree, std::ios_base::failure);
|
||||
}
|
||||
|
||||
TEST(merkletree, testZeroElements) {
|
||||
for (int start = 0; start < 20; start++) {
|
||||
ZCIncrementalMerkleTree newTree;
|
||||
SproutMerkleTree newTree;
|
||||
|
||||
ASSERT_TRUE(newTree.root() == ZCIncrementalMerkleTree::empty_root());
|
||||
ASSERT_TRUE(newTree.root() == SproutMerkleTree::empty_root());
|
||||
|
||||
for (int i = start; i > 0; i--) {
|
||||
newTree.append(uint256S("54d626e08c1c802b305dad30b7e54a82f102390cc92c7d4db112048935236e9c"));
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include "sodium.h"
|
||||
|
||||
#include <array>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "zcash/Note.hpp"
|
||||
#include "zcash/NoteEncryption.hpp"
|
||||
#include "zcash/prf.h"
|
||||
#include "zcash/Address.hpp"
|
||||
#include "crypto/sha256.h"
|
||||
#include "librustzcash.h"
|
||||
|
||||
class TestNoteDecryption : public ZCNoteDecryption {
|
||||
public:
|
||||
@@ -16,6 +20,327 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
TEST(noteencryption, NotePlaintext)
|
||||
{
|
||||
using namespace libzcash;
|
||||
auto xsk = SaplingSpendingKey(uint256()).expanded_spending_key();
|
||||
auto fvk = xsk.full_viewing_key();
|
||||
auto ivk = fvk.in_viewing_key();
|
||||
SaplingPaymentAddress addr = *ivk.address({0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
|
||||
|
||||
std::array<unsigned char, ZC_MEMO_SIZE> memo;
|
||||
for (size_t i = 0; i < ZC_MEMO_SIZE; i++) {
|
||||
// Fill the message with dummy data
|
||||
memo[i] = (unsigned char) i;
|
||||
}
|
||||
|
||||
SaplingNote note(addr, 39393);
|
||||
auto cmu_opt = note.cm();
|
||||
if (!cmu_opt) {
|
||||
FAIL();
|
||||
}
|
||||
uint256 cmu = cmu_opt.get();
|
||||
SaplingNotePlaintext pt(note, memo);
|
||||
|
||||
auto res = pt.encrypt(addr.pk_d);
|
||||
if (!res) {
|
||||
FAIL();
|
||||
}
|
||||
|
||||
auto enc = res.get();
|
||||
|
||||
auto ct = enc.first;
|
||||
auto encryptor = enc.second;
|
||||
auto epk = encryptor.get_epk();
|
||||
|
||||
// Try to decrypt with incorrect commitment
|
||||
ASSERT_FALSE(SaplingNotePlaintext::decrypt(
|
||||
ct,
|
||||
ivk,
|
||||
epk,
|
||||
uint256()
|
||||
));
|
||||
|
||||
// Try to decrypt with correct commitment
|
||||
auto foo = SaplingNotePlaintext::decrypt(
|
||||
ct,
|
||||
ivk,
|
||||
epk,
|
||||
cmu
|
||||
);
|
||||
|
||||
if (!foo) {
|
||||
FAIL();
|
||||
}
|
||||
|
||||
auto bar = foo.get();
|
||||
|
||||
ASSERT_TRUE(bar.value() == pt.value());
|
||||
ASSERT_TRUE(bar.memo() == pt.memo());
|
||||
ASSERT_TRUE(bar.d == pt.d);
|
||||
ASSERT_TRUE(bar.rcm == pt.rcm);
|
||||
|
||||
auto foobar = bar.note(ivk);
|
||||
|
||||
if (!foobar) {
|
||||
FAIL();
|
||||
}
|
||||
|
||||
auto new_note = foobar.get();
|
||||
|
||||
ASSERT_TRUE(note.value() == new_note.value());
|
||||
ASSERT_TRUE(note.d == new_note.d);
|
||||
ASSERT_TRUE(note.pk_d == new_note.pk_d);
|
||||
ASSERT_TRUE(note.r == new_note.r);
|
||||
ASSERT_TRUE(note.cm() == new_note.cm());
|
||||
|
||||
SaplingOutgoingPlaintext out_pt;
|
||||
out_pt.pk_d = note.pk_d;
|
||||
out_pt.esk = encryptor.get_esk();
|
||||
|
||||
auto ovk = random_uint256();
|
||||
auto cv = random_uint256();
|
||||
auto cm = random_uint256();
|
||||
|
||||
auto out_ct = out_pt.encrypt(
|
||||
ovk,
|
||||
cv,
|
||||
cm,
|
||||
encryptor
|
||||
);
|
||||
|
||||
auto decrypted_out_ct = out_pt.decrypt(
|
||||
out_ct,
|
||||
ovk,
|
||||
cv,
|
||||
cm,
|
||||
encryptor.get_epk()
|
||||
);
|
||||
|
||||
if (!decrypted_out_ct) {
|
||||
FAIL();
|
||||
}
|
||||
|
||||
auto decrypted_out_ct_unwrapped = decrypted_out_ct.get();
|
||||
|
||||
ASSERT_TRUE(decrypted_out_ct_unwrapped.pk_d == out_pt.pk_d);
|
||||
ASSERT_TRUE(decrypted_out_ct_unwrapped.esk == out_pt.esk);
|
||||
|
||||
// Test sender won't accept invalid commitments
|
||||
ASSERT_FALSE(
|
||||
SaplingNotePlaintext::decrypt(
|
||||
ct,
|
||||
epk,
|
||||
decrypted_out_ct_unwrapped.esk,
|
||||
decrypted_out_ct_unwrapped.pk_d,
|
||||
uint256()
|
||||
)
|
||||
);
|
||||
|
||||
// Test sender can decrypt the note ciphertext.
|
||||
foo = SaplingNotePlaintext::decrypt(
|
||||
ct,
|
||||
epk,
|
||||
decrypted_out_ct_unwrapped.esk,
|
||||
decrypted_out_ct_unwrapped.pk_d,
|
||||
cmu
|
||||
);
|
||||
|
||||
if (!foo) {
|
||||
FAIL();
|
||||
}
|
||||
|
||||
bar = foo.get();
|
||||
|
||||
ASSERT_TRUE(bar.value() == pt.value());
|
||||
ASSERT_TRUE(bar.memo() == pt.memo());
|
||||
ASSERT_TRUE(bar.d == pt.d);
|
||||
ASSERT_TRUE(bar.rcm == pt.rcm);
|
||||
}
|
||||
|
||||
TEST(noteencryption, SaplingApi)
|
||||
{
|
||||
using namespace libzcash;
|
||||
|
||||
// Create recipient addresses
|
||||
auto sk = SaplingSpendingKey(uint256()).expanded_spending_key();
|
||||
auto vk = sk.full_viewing_key();
|
||||
auto ivk = vk.in_viewing_key();
|
||||
SaplingPaymentAddress pk_1 = *ivk.address({0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
|
||||
SaplingPaymentAddress pk_2 = *ivk.address({4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
|
||||
|
||||
// Blob of stuff we're encrypting
|
||||
std::array<unsigned char, ZC_SAPLING_ENCPLAINTEXT_SIZE> message;
|
||||
for (size_t i = 0; i < ZC_SAPLING_ENCPLAINTEXT_SIZE; i++) {
|
||||
// Fill the message with dummy data
|
||||
message[i] = (unsigned char) i;
|
||||
}
|
||||
|
||||
std::array<unsigned char, ZC_SAPLING_OUTPLAINTEXT_SIZE> small_message;
|
||||
for (size_t i = 0; i < ZC_SAPLING_OUTPLAINTEXT_SIZE; i++) {
|
||||
// Fill the message with dummy data
|
||||
small_message[i] = (unsigned char) i;
|
||||
}
|
||||
|
||||
// Invalid diversifier
|
||||
ASSERT_EQ(boost::none, SaplingNoteEncryption::FromDiversifier({1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}));
|
||||
|
||||
// Encrypt to pk_1
|
||||
auto enc = *SaplingNoteEncryption::FromDiversifier(pk_1.d);
|
||||
auto ciphertext_1 = *enc.encrypt_to_recipient(
|
||||
pk_1.pk_d,
|
||||
message
|
||||
);
|
||||
auto epk_1 = enc.get_epk();
|
||||
{
|
||||
uint256 test_epk;
|
||||
uint256 test_esk = enc.get_esk();
|
||||
ASSERT_TRUE(librustzcash_sapling_ka_derivepublic(pk_1.d.begin(), test_esk.begin(), test_epk.begin()));
|
||||
ASSERT_TRUE(test_epk == epk_1);
|
||||
}
|
||||
auto cv_1 = random_uint256();
|
||||
auto cm_1 = random_uint256();
|
||||
auto out_ciphertext_1 = enc.encrypt_to_ourselves(
|
||||
sk.ovk,
|
||||
cv_1,
|
||||
cm_1,
|
||||
small_message
|
||||
);
|
||||
|
||||
// Encrypt to pk_2
|
||||
enc = *SaplingNoteEncryption::FromDiversifier(pk_2.d);
|
||||
auto ciphertext_2 = *enc.encrypt_to_recipient(
|
||||
pk_2.pk_d,
|
||||
message
|
||||
);
|
||||
auto epk_2 = enc.get_epk();
|
||||
|
||||
auto cv_2 = random_uint256();
|
||||
auto cm_2 = random_uint256();
|
||||
auto out_ciphertext_2 = enc.encrypt_to_ourselves(
|
||||
sk.ovk,
|
||||
cv_2,
|
||||
cm_2,
|
||||
small_message
|
||||
);
|
||||
|
||||
// Test nonce-reuse resistance of API
|
||||
{
|
||||
auto tmp_enc = *SaplingNoteEncryption::FromDiversifier(pk_1.d);
|
||||
|
||||
tmp_enc.encrypt_to_recipient(
|
||||
pk_1.pk_d,
|
||||
message
|
||||
);
|
||||
|
||||
ASSERT_THROW(tmp_enc.encrypt_to_recipient(
|
||||
pk_1.pk_d,
|
||||
message
|
||||
), std::logic_error);
|
||||
|
||||
tmp_enc.encrypt_to_ourselves(
|
||||
sk.ovk,
|
||||
cv_2,
|
||||
cm_2,
|
||||
small_message
|
||||
);
|
||||
|
||||
ASSERT_THROW(tmp_enc.encrypt_to_ourselves(
|
||||
sk.ovk,
|
||||
cv_2,
|
||||
cm_2,
|
||||
small_message
|
||||
), std::logic_error);
|
||||
}
|
||||
|
||||
// Try to decrypt
|
||||
auto plaintext_1 = *AttemptSaplingEncDecryption(
|
||||
ciphertext_1,
|
||||
ivk,
|
||||
epk_1
|
||||
);
|
||||
ASSERT_TRUE(message == plaintext_1);
|
||||
|
||||
auto small_plaintext_1 = *AttemptSaplingOutDecryption(
|
||||
out_ciphertext_1,
|
||||
sk.ovk,
|
||||
cv_1,
|
||||
cm_1,
|
||||
epk_1
|
||||
);
|
||||
ASSERT_TRUE(small_message == small_plaintext_1);
|
||||
|
||||
auto plaintext_2 = *AttemptSaplingEncDecryption(
|
||||
ciphertext_2,
|
||||
ivk,
|
||||
epk_2
|
||||
);
|
||||
ASSERT_TRUE(message == plaintext_2);
|
||||
|
||||
auto small_plaintext_2 = *AttemptSaplingOutDecryption(
|
||||
out_ciphertext_2,
|
||||
sk.ovk,
|
||||
cv_2,
|
||||
cm_2,
|
||||
epk_2
|
||||
);
|
||||
ASSERT_TRUE(small_message == small_plaintext_2);
|
||||
|
||||
// Try to decrypt out ciphertext with wrong key material
|
||||
ASSERT_FALSE(AttemptSaplingOutDecryption(
|
||||
out_ciphertext_1,
|
||||
random_uint256(),
|
||||
cv_1,
|
||||
cm_1,
|
||||
epk_1
|
||||
));
|
||||
ASSERT_FALSE(AttemptSaplingOutDecryption(
|
||||
out_ciphertext_1,
|
||||
sk.ovk,
|
||||
random_uint256(),
|
||||
cm_1,
|
||||
epk_1
|
||||
));
|
||||
ASSERT_FALSE(AttemptSaplingOutDecryption(
|
||||
out_ciphertext_1,
|
||||
sk.ovk,
|
||||
cv_1,
|
||||
random_uint256(),
|
||||
epk_1
|
||||
));
|
||||
ASSERT_FALSE(AttemptSaplingOutDecryption(
|
||||
out_ciphertext_1,
|
||||
sk.ovk,
|
||||
cv_1,
|
||||
cm_1,
|
||||
random_uint256()
|
||||
));
|
||||
|
||||
// Try to decrypt with wrong ephemeral key
|
||||
ASSERT_FALSE(AttemptSaplingEncDecryption(
|
||||
ciphertext_1,
|
||||
ivk,
|
||||
epk_2
|
||||
));
|
||||
ASSERT_FALSE(AttemptSaplingEncDecryption(
|
||||
ciphertext_2,
|
||||
ivk,
|
||||
epk_1
|
||||
));
|
||||
|
||||
// Try to decrypt with wrong ivk
|
||||
ASSERT_FALSE(AttemptSaplingEncDecryption(
|
||||
ciphertext_1,
|
||||
uint256(),
|
||||
epk_1
|
||||
));
|
||||
ASSERT_FALSE(AttemptSaplingEncDecryption(
|
||||
ciphertext_2,
|
||||
uint256(),
|
||||
epk_2
|
||||
));
|
||||
}
|
||||
|
||||
TEST(noteencryption, api)
|
||||
{
|
||||
uint256 sk_enc = ZCNoteEncryption::generate_privkey(uint252(uint256S("21035d60bc1983e37950ce4803418a8fb33ea68d5b937ca382ecbae7564d6a07")));
|
||||
@@ -29,7 +354,7 @@ TEST(noteencryption, api)
|
||||
ASSERT_TRUE(b.get_epk() != c.get_epk());
|
||||
}
|
||||
|
||||
boost::array<unsigned char, ZC_NOTEPLAINTEXT_SIZE> message;
|
||||
std::array<unsigned char, ZC_NOTEPLAINTEXT_SIZE> message;
|
||||
for (size_t i = 0; i < ZC_NOTEPLAINTEXT_SIZE; i++) {
|
||||
// Fill the message with dummy data
|
||||
message[i] = (unsigned char) i;
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include "zcash/Address.hpp"
|
||||
#include "wallet/wallet.h"
|
||||
#include "amount.h"
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <set>
|
||||
@@ -91,14 +93,13 @@ public:
|
||||
// Note that the zpd: prefix is not part of the payment disclosure blob itself. It is only
|
||||
// used as convention to improve the user experience when sharing payment disclosure blobs.
|
||||
TEST(paymentdisclosure, mainnet) {
|
||||
ECC_Start();
|
||||
SelectParams(CBaseChainParams::MAIN);
|
||||
|
||||
boost::filesystem::path pathTemp = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
|
||||
boost::filesystem::create_directories(pathTemp);
|
||||
mapArgs["-datadir"] = pathTemp.string();
|
||||
|
||||
std::cout << "Test payment disclosure database created in folder: " << pathTemp.native() << std::endl;
|
||||
std::cout << "Test payment disclosure database created in folder: " << pathTemp.string() << std::endl;
|
||||
|
||||
PaymentDisclosureDBTest mydb(pathTemp);
|
||||
|
||||
@@ -120,7 +121,7 @@ TEST(paymentdisclosure, mainnet) {
|
||||
PaymentDisclosureInfo info;
|
||||
info.esk = random_uint256();
|
||||
info.joinSplitPrivKey = joinSplitPrivKey;
|
||||
info.zaddr = libzcash::SpendingKey::random().address();
|
||||
info.zaddr = libzcash::SproutSpendingKey::random().address();
|
||||
ASSERT_TRUE(mydb.Put(key, info));
|
||||
|
||||
// Retrieve info from test database into new local variable and test it matches
|
||||
@@ -131,7 +132,7 @@ TEST(paymentdisclosure, mainnet) {
|
||||
// Modify this local variable and confirm it no longer matches
|
||||
info2.esk = random_uint256();
|
||||
info2.joinSplitPrivKey = random_uint256();
|
||||
info2.zaddr = libzcash::SpendingKey::random().address();
|
||||
info2.zaddr = libzcash::SproutSpendingKey::random().address();
|
||||
ASSERT_NE(info, info2);
|
||||
|
||||
// Using the payment info object, let's create a dummy payload
|
||||
@@ -167,7 +168,7 @@ TEST(paymentdisclosure, mainnet) {
|
||||
}
|
||||
|
||||
// Convert signature buffer to boost array
|
||||
boost::array<unsigned char, 64> arrayPayloadSig;
|
||||
std::array<unsigned char, 64> arrayPayloadSig;
|
||||
memcpy(arrayPayloadSig.data(), &payloadSig[0], 64);
|
||||
|
||||
// Payment disclosure blob to pass around
|
||||
@@ -207,6 +208,4 @@ TEST(paymentdisclosure, mainnet) {
|
||||
#if DUMP_DATABASE_TO_STDOUT == true
|
||||
mydb.DebugDumpAllStdout();
|
||||
#endif
|
||||
|
||||
ECC_Stop();
|
||||
}
|
||||
|
||||
15
src/gtest/test_pedersen_hash.cpp
Normal file
15
src/gtest/test_pedersen_hash.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include "librustzcash.h"
|
||||
#include "uint256.h"
|
||||
|
||||
TEST(PedersenHash, TestAPI) {
|
||||
const uint256 a = uint256S("87a086ae7d2252d58729b30263fb7b66308bf94ef59a76c9c86e7ea016536505");
|
||||
const uint256 b = uint256S("a75b84a125b2353da7e8d96ee2a15efe4de23df9601b9d9564ba59de57130406");
|
||||
uint256 result;
|
||||
|
||||
librustzcash_merkle_hash(25, a.begin(), b.begin(), result.begin());
|
||||
|
||||
uint256 expected_result = uint256S("5bf43b5736c19b714d1f462c9d22ba3492c36e3d9bbd7ca24d94b440550aa561");
|
||||
|
||||
ASSERT_TRUE(result == expected_result);
|
||||
}
|
||||
@@ -241,7 +241,7 @@ TEST(proofs, sqrt_fq2)
|
||||
|
||||
TEST(proofs, size_is_expected)
|
||||
{
|
||||
ZCProof p;
|
||||
PHGRProof p;
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << p;
|
||||
|
||||
@@ -444,7 +444,7 @@ TEST(proofs, zksnark_serializes_properly)
|
||||
auto vkprecomp = libsnark::r1cs_ppzksnark_verifier_process_vk(kp.vk);
|
||||
|
||||
for (size_t i = 0; i < 20; i++) {
|
||||
auto badproof = ZCProof::random_invalid();
|
||||
auto badproof = PHGRProof::random_invalid();
|
||||
auto proof = badproof.to_libsnark_proof<libsnark::r1cs_ppzksnark_proof<curve_pp>>();
|
||||
|
||||
auto verifierEnabled = ProofVerifier::Strict();
|
||||
@@ -496,12 +496,12 @@ TEST(proofs, zksnark_serializes_properly)
|
||||
proof
|
||||
));
|
||||
|
||||
ZCProof compressed_proof_0(proof);
|
||||
PHGRProof compressed_proof_0(proof);
|
||||
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << compressed_proof_0;
|
||||
|
||||
ZCProof compressed_proof_1;
|
||||
PHGRProof compressed_proof_1;
|
||||
ss >> compressed_proof_1;
|
||||
|
||||
ASSERT_TRUE(compressed_proof_0 == compressed_proof_1);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "chainparams.h"
|
||||
#include "clientversion.h"
|
||||
#include "primitives/block.h"
|
||||
#include "rpcserver.h"
|
||||
#include "rpc/server.h"
|
||||
#include "streams.h"
|
||||
#include "utilstrencodings.h"
|
||||
|
||||
|
||||
72
src/gtest/test_sapling_note.cpp
Normal file
72
src/gtest/test_sapling_note.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "zcash/Address.hpp"
|
||||
#include "zcash/Note.hpp"
|
||||
|
||||
#include "amount.h"
|
||||
#include "random.h"
|
||||
#include "librustzcash.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
using namespace libzcash;
|
||||
|
||||
// Test data from https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_key_components.py
|
||||
TEST(SaplingNote, TestVectors)
|
||||
{
|
||||
uint64_t v = 0;
|
||||
uint64_t note_pos = 0;
|
||||
std::array<uint8_t, 11> diversifier{0xf1, 0x9d, 0x9b, 0x79, 0x7e, 0x39, 0xf3, 0x37, 0x44, 0x58, 0x39};
|
||||
std::vector<uint8_t> v_sk{
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00};
|
||||
std::vector<uint8_t> v_pk_d{
|
||||
0xdb, 0x4c, 0xd2, 0xb0, 0xaa, 0xc4, 0xf7, 0xeb, 0x8c, 0xa1, 0x31, 0xf1, 0x65, 0x67,
|
||||
0xc4, 0x45, 0xa9, 0x55, 0x51, 0x26, 0xd3, 0xc2, 0x9f, 0x14, 0xe3, 0xd7, 0x76, 0xe8,
|
||||
0x41, 0xae, 0x74, 0x15};
|
||||
std::vector<uint8_t> v_r{
|
||||
0x39, 0x17, 0x6d, 0xac, 0x39, 0xac, 0xe4, 0x98, 0x0e, 0xcc, 0x8d, 0x77, 0x8e, 0x89,
|
||||
0x86, 0x02, 0x55, 0xec, 0x36, 0x15, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00};
|
||||
std::vector<uint8_t> v_cm{
|
||||
0xcb, 0x3c, 0xf9, 0x15, 0x32, 0x70, 0xd5, 0x7e, 0xb9, 0x14, 0xc6, 0xc2, 0xbc, 0xc0,
|
||||
0x18, 0x50, 0xc9, 0xfe, 0xd4, 0x4f, 0xce, 0x08, 0x06, 0x27, 0x8f, 0x08, 0x3e, 0xf2,
|
||||
0xdd, 0x07, 0x64, 0x39};
|
||||
std::vector<uint8_t> v_nf{
|
||||
0x44, 0xfa, 0xd6, 0x56, 0x4f, 0xfd, 0xec, 0x9f, 0xa1, 0x9c, 0x43, 0xa2, 0x8f, 0x86,
|
||||
0x1d, 0x5e, 0xbf, 0x60, 0x23, 0x46, 0x00, 0x7d, 0xe7, 0x62, 0x67, 0xd9, 0x75, 0x27,
|
||||
0x47, 0xab, 0x40, 0x63};
|
||||
uint256 sk(v_sk);
|
||||
uint256 pk_d(v_pk_d);
|
||||
uint256 r(v_r);
|
||||
uint256 cm(v_cm);
|
||||
uint256 nf(v_nf);
|
||||
|
||||
// Test commitment
|
||||
SaplingNote note = SaplingNote(diversifier, pk_d, v, r);
|
||||
ASSERT_EQ(note.cm().get(), cm);
|
||||
|
||||
// Test nullifier
|
||||
SaplingSpendingKey spendingKey(sk);
|
||||
ASSERT_EQ(note.nullifier(spendingKey.full_viewing_key(), note_pos), nf);
|
||||
}
|
||||
|
||||
|
||||
TEST(SaplingNote, Random)
|
||||
{
|
||||
// Test creating random notes using the same spending key
|
||||
auto address = SaplingSpendingKey::random().default_address();
|
||||
SaplingNote note1(address, GetRand(MAX_MONEY));
|
||||
SaplingNote note2(address, GetRand(MAX_MONEY));
|
||||
|
||||
ASSERT_EQ(note1.d, note2.d);
|
||||
ASSERT_EQ(note1.pk_d, note2.pk_d);
|
||||
ASSERT_NE(note1.value(), note2.value());
|
||||
ASSERT_NE(note1.r, note2.r);
|
||||
|
||||
// Test diversifier and pk_d are not the same for different spending keys
|
||||
SaplingNote note3(SaplingSpendingKey::random().default_address(), GetRand(MAX_MONEY));
|
||||
ASSERT_NE(note1.d, note3.d);
|
||||
ASSERT_NE(note1.pk_d, note3.pk_d);
|
||||
}
|
||||
@@ -4,18 +4,20 @@
|
||||
#include "zcash/Note.hpp"
|
||||
#include "zcash/Address.hpp"
|
||||
|
||||
#include <array>
|
||||
|
||||
extern ZCJoinSplit* params;
|
||||
extern int GenZero(int n);
|
||||
extern int GenMax(int n);
|
||||
|
||||
TEST(Transaction, JSDescriptionRandomized) {
|
||||
// construct a merkle tree
|
||||
ZCIncrementalMerkleTree merkleTree;
|
||||
SproutMerkleTree merkleTree;
|
||||
|
||||
libzcash::SpendingKey k = libzcash::SpendingKey::random();
|
||||
libzcash::PaymentAddress addr = k.address();
|
||||
libzcash::SproutSpendingKey k = libzcash::SproutSpendingKey::random();
|
||||
libzcash::SproutPaymentAddress addr = k.address();
|
||||
|
||||
libzcash::Note note(addr.a_pk, 100, uint256(), uint256());
|
||||
libzcash::SproutNote note(addr.a_pk, 100, uint256(), uint256());
|
||||
|
||||
// commitment from coin
|
||||
uint256 commitment = note.cm();
|
||||
@@ -29,81 +31,59 @@ TEST(Transaction, JSDescriptionRandomized) {
|
||||
auto witness = merkleTree.witness();
|
||||
|
||||
// create JSDescription
|
||||
uint256 pubKeyHash;
|
||||
boost::array<libzcash::JSInput, ZC_NUM_JS_INPUTS> inputs = {
|
||||
uint256 joinSplitPubKey;
|
||||
std::array<libzcash::JSInput, ZC_NUM_JS_INPUTS> inputs = {
|
||||
libzcash::JSInput(witness, note, k),
|
||||
libzcash::JSInput() // dummy input of zero value
|
||||
};
|
||||
boost::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS> outputs = {
|
||||
std::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS> outputs = {
|
||||
libzcash::JSOutput(addr, 50),
|
||||
libzcash::JSOutput(addr, 50)
|
||||
};
|
||||
#ifdef __LP64__ // required for building on MacOS
|
||||
boost::array<uint64_t, ZC_NUM_JS_INPUTS> inputMap;
|
||||
boost::array<uint64_t, ZC_NUM_JS_OUTPUTS> outputMap;
|
||||
#else
|
||||
boost::array<size_t, ZC_NUM_JS_INPUTS> inputMap;
|
||||
boost::array<size_t, ZC_NUM_JS_OUTPUTS> outputMap;
|
||||
#endif
|
||||
std::array<size_t, ZC_NUM_JS_INPUTS> inputMap;
|
||||
std::array<size_t, ZC_NUM_JS_OUTPUTS> outputMap;
|
||||
|
||||
{
|
||||
auto jsdesc = JSDescription::Randomized(
|
||||
*params, pubKeyHash, rt,
|
||||
false,
|
||||
*params, joinSplitPubKey, rt,
|
||||
inputs, outputs,
|
||||
inputMap, outputMap,
|
||||
0, 0, false);
|
||||
|
||||
#ifdef __LP64__ // required for building on MacOS
|
||||
std::set<uint64_t> inputSet(inputMap.begin(), inputMap.end());
|
||||
std::set<uint64_t> expectedInputSet {0, 1};
|
||||
#else
|
||||
std::set<size_t> inputSet(inputMap.begin(), inputMap.end());
|
||||
std::set<size_t> expectedInputSet {0, 1};
|
||||
#endif
|
||||
EXPECT_EQ(expectedInputSet, inputSet);
|
||||
|
||||
#ifdef __LP64__ // required for building on MacOS
|
||||
std::set<uint64_t> outputSet(outputMap.begin(), outputMap.end());
|
||||
std::set<uint64_t> expectedOutputSet {0, 1};
|
||||
#else
|
||||
std::set<size_t> outputSet(outputMap.begin(), outputMap.end());
|
||||
std::set<size_t> expectedOutputSet {0, 1};
|
||||
#endif
|
||||
EXPECT_EQ(expectedOutputSet, outputSet);
|
||||
}
|
||||
|
||||
{
|
||||
auto jsdesc = JSDescription::Randomized(
|
||||
*params, pubKeyHash, rt,
|
||||
false,
|
||||
*params, joinSplitPubKey, rt,
|
||||
inputs, outputs,
|
||||
inputMap, outputMap,
|
||||
0, 0, false, nullptr, GenZero);
|
||||
|
||||
#ifdef __LP64__ // required for building on MacOS
|
||||
boost::array<uint64_t, ZC_NUM_JS_INPUTS> expectedInputMap {1, 0};
|
||||
boost::array<uint64_t, ZC_NUM_JS_OUTPUTS> expectedOutputMap {1, 0};
|
||||
#else
|
||||
boost::array<size_t, ZC_NUM_JS_INPUTS> expectedInputMap {1, 0};
|
||||
boost::array<size_t, ZC_NUM_JS_OUTPUTS> expectedOutputMap {1, 0};
|
||||
#endif
|
||||
std::array<size_t, ZC_NUM_JS_INPUTS> expectedInputMap {1, 0};
|
||||
std::array<size_t, ZC_NUM_JS_OUTPUTS> expectedOutputMap {1, 0};
|
||||
EXPECT_EQ(expectedInputMap, inputMap);
|
||||
EXPECT_EQ(expectedOutputMap, outputMap);
|
||||
}
|
||||
|
||||
{
|
||||
auto jsdesc = JSDescription::Randomized(
|
||||
*params, pubKeyHash, rt,
|
||||
false,
|
||||
*params, joinSplitPubKey, rt,
|
||||
inputs, outputs,
|
||||
inputMap, outputMap,
|
||||
0, 0, false, nullptr, GenMax);
|
||||
|
||||
#ifdef __LP64__ // required for building on MacOS
|
||||
boost::array<uint64_t, ZC_NUM_JS_INPUTS> expectedInputMap {0, 1};
|
||||
boost::array<uint64_t, ZC_NUM_JS_OUTPUTS> expectedOutputMap {0, 1};
|
||||
#else
|
||||
boost::array<size_t, ZC_NUM_JS_INPUTS> expectedInputMap {0, 1};
|
||||
boost::array<size_t, ZC_NUM_JS_OUTPUTS> expectedOutputMap {0, 1};
|
||||
#endif
|
||||
std::array<size_t, ZC_NUM_JS_INPUTS> expectedInputMap {0, 1};
|
||||
std::array<size_t, ZC_NUM_JS_OUTPUTS> expectedOutputMap {0, 1};
|
||||
EXPECT_EQ(expectedInputMap, inputMap);
|
||||
EXPECT_EQ(expectedOutputMap, outputMap);
|
||||
}
|
||||
|
||||
335
src/gtest/test_transaction_builder.cpp
Normal file
335
src/gtest/test_transaction_builder.cpp
Normal file
@@ -0,0 +1,335 @@
|
||||
#include "chainparams.h"
|
||||
#include "consensus/params.h"
|
||||
#include "consensus/validation.h"
|
||||
#include "key_io.h"
|
||||
#include "main.h"
|
||||
#include "pubkey.h"
|
||||
#include "transaction_builder.h"
|
||||
#include "zcash/Address.hpp"
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
static const std::string tSecretRegtest = "cND2ZvtabDbJ1gucx9GWH6XT9kgTAqfb6cotPt5Q5CyxVDhid2EN";
|
||||
|
||||
TEST(TransactionBuilder, Invoke)
|
||||
{
|
||||
SelectParams(CBaseChainParams::REGTEST);
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
|
||||
auto consensusParams = Params().GetConsensus();
|
||||
|
||||
CBasicKeyStore keystore;
|
||||
CKey tsk = DecodeSecret(tSecretRegtest);
|
||||
keystore.AddKey(tsk);
|
||||
auto scriptPubKey = GetScriptForDestination(tsk.GetPubKey().GetID());
|
||||
|
||||
auto sk_from = libzcash::SaplingSpendingKey::random();
|
||||
auto fvk_from = sk_from.full_viewing_key();
|
||||
|
||||
auto sk = libzcash::SaplingSpendingKey::random();
|
||||
auto expsk = sk.expanded_spending_key();
|
||||
auto fvk = sk.full_viewing_key();
|
||||
auto ivk = fvk.in_viewing_key();
|
||||
libzcash::diversifier_t d = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
auto pk = *ivk.address(d);
|
||||
|
||||
// Create a shielding transaction from transparent to Sapling
|
||||
// 0.0005 t-ZEC in, 0.0004 z-ZEC out, 0.0001 t-ZEC fee
|
||||
auto builder1 = TransactionBuilder(consensusParams, 1, &keystore);
|
||||
builder1.AddTransparentInput(COutPoint(), scriptPubKey, 50000);
|
||||
builder1.AddSaplingOutput(fvk_from, pk, 40000, {});
|
||||
auto maybe_tx1 = builder1.Build();
|
||||
ASSERT_EQ(static_cast<bool>(maybe_tx1), true);
|
||||
auto tx1 = maybe_tx1.get();
|
||||
|
||||
EXPECT_EQ(tx1.vin.size(), 1);
|
||||
EXPECT_EQ(tx1.vout.size(), 0);
|
||||
EXPECT_EQ(tx1.vjoinsplit.size(), 0);
|
||||
EXPECT_EQ(tx1.vShieldedSpend.size(), 0);
|
||||
EXPECT_EQ(tx1.vShieldedOutput.size(), 1);
|
||||
EXPECT_EQ(tx1.valueBalance, -40000);
|
||||
|
||||
CValidationState state;
|
||||
EXPECT_TRUE(ContextualCheckTransaction(tx1, state, 2, 0));
|
||||
EXPECT_EQ(state.GetRejectReason(), "");
|
||||
|
||||
// Prepare to spend the note that was just created
|
||||
auto maybe_pt = libzcash::SaplingNotePlaintext::decrypt(
|
||||
tx1.vShieldedOutput[0].encCiphertext, ivk, tx1.vShieldedOutput[0].ephemeralKey, tx1.vShieldedOutput[0].cm);
|
||||
ASSERT_EQ(static_cast<bool>(maybe_pt), true);
|
||||
auto maybe_note = maybe_pt.get().note(ivk);
|
||||
ASSERT_EQ(static_cast<bool>(maybe_note), true);
|
||||
auto note = maybe_note.get();
|
||||
SaplingMerkleTree tree;
|
||||
tree.append(tx1.vShieldedOutput[0].cm);
|
||||
auto anchor = tree.root();
|
||||
auto witness = tree.witness();
|
||||
|
||||
// Create a Sapling-only transaction
|
||||
// 0.0004 z-ZEC in, 0.00025 z-ZEC out, 0.0001 t-ZEC fee, 0.00005 z-ZEC change
|
||||
auto builder2 = TransactionBuilder(consensusParams, 2);
|
||||
ASSERT_TRUE(builder2.AddSaplingSpend(expsk, note, anchor, witness));
|
||||
// Check that trying to add a different anchor fails
|
||||
ASSERT_FALSE(builder2.AddSaplingSpend(expsk, note, uint256(), witness));
|
||||
|
||||
builder2.AddSaplingOutput(fvk, pk, 25000, {});
|
||||
auto maybe_tx2 = builder2.Build();
|
||||
ASSERT_EQ(static_cast<bool>(maybe_tx2), true);
|
||||
auto tx2 = maybe_tx2.get();
|
||||
|
||||
EXPECT_EQ(tx2.vin.size(), 0);
|
||||
EXPECT_EQ(tx2.vout.size(), 0);
|
||||
EXPECT_EQ(tx2.vjoinsplit.size(), 0);
|
||||
EXPECT_EQ(tx2.vShieldedSpend.size(), 1);
|
||||
EXPECT_EQ(tx2.vShieldedOutput.size(), 2);
|
||||
EXPECT_EQ(tx2.valueBalance, 10000);
|
||||
|
||||
EXPECT_TRUE(ContextualCheckTransaction(tx2, state, 3, 0));
|
||||
EXPECT_EQ(state.GetRejectReason(), "");
|
||||
|
||||
// Revert to default
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
|
||||
}
|
||||
|
||||
TEST(TransactionBuilder, ThrowsOnTransparentInputWithoutKeyStore)
|
||||
{
|
||||
auto consensusParams = Params().GetConsensus();
|
||||
|
||||
auto builder = TransactionBuilder(consensusParams, 1);
|
||||
ASSERT_THROW(builder.AddTransparentInput(COutPoint(), CScript(), 1), std::runtime_error);
|
||||
}
|
||||
|
||||
TEST(TransactionBuilder, RejectsInvalidTransparentOutput)
|
||||
{
|
||||
auto consensusParams = Params().GetConsensus();
|
||||
|
||||
// Default CTxDestination type is an invalid address
|
||||
CTxDestination taddr;
|
||||
auto builder = TransactionBuilder(consensusParams, 1);
|
||||
EXPECT_FALSE(builder.AddTransparentOutput(taddr, 50));
|
||||
}
|
||||
|
||||
TEST(TransactionBuilder, RejectsInvalidTransparentChangeAddress)
|
||||
{
|
||||
auto consensusParams = Params().GetConsensus();
|
||||
|
||||
// Default CTxDestination type is an invalid address
|
||||
CTxDestination taddr;
|
||||
auto builder = TransactionBuilder(consensusParams, 1);
|
||||
EXPECT_FALSE(builder.SendChangeTo(taddr));
|
||||
}
|
||||
|
||||
TEST(TransactionBuilder, FailsWithNegativeChange)
|
||||
{
|
||||
SelectParams(CBaseChainParams::REGTEST);
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
|
||||
auto consensusParams = Params().GetConsensus();
|
||||
|
||||
// Generate dummy Sapling address
|
||||
auto sk = libzcash::SaplingSpendingKey::random();
|
||||
auto expsk = sk.expanded_spending_key();
|
||||
auto fvk = sk.full_viewing_key();
|
||||
auto pk = sk.default_address();
|
||||
|
||||
// Set up dummy transparent address
|
||||
CBasicKeyStore keystore;
|
||||
CKey tsk = DecodeSecret(tSecretRegtest);
|
||||
keystore.AddKey(tsk);
|
||||
auto tkeyid = tsk.GetPubKey().GetID();
|
||||
auto scriptPubKey = GetScriptForDestination(tkeyid);
|
||||
CTxDestination taddr = tkeyid;
|
||||
|
||||
// Generate dummy Sapling note
|
||||
libzcash::SaplingNote note(pk, 59999);
|
||||
auto cm = note.cm().value();
|
||||
SaplingMerkleTree tree;
|
||||
tree.append(cm);
|
||||
auto anchor = tree.root();
|
||||
auto witness = tree.witness();
|
||||
|
||||
// Fail if there is only a Sapling output
|
||||
// 0.0005 z-ZEC out, 0.0001 t-ZEC fee
|
||||
auto builder = TransactionBuilder(consensusParams, 1);
|
||||
builder.AddSaplingOutput(fvk, pk, 50000, {});
|
||||
EXPECT_FALSE(static_cast<bool>(builder.Build()));
|
||||
|
||||
// Fail if there is only a transparent output
|
||||
// 0.0005 t-ZEC out, 0.0001 t-ZEC fee
|
||||
builder = TransactionBuilder(consensusParams, 1, &keystore);
|
||||
EXPECT_TRUE(builder.AddTransparentOutput(taddr, 50000));
|
||||
EXPECT_FALSE(static_cast<bool>(builder.Build()));
|
||||
|
||||
// Fails if there is insufficient input
|
||||
// 0.0005 t-ZEC out, 0.0001 t-ZEC fee, 0.00059999 z-ZEC in
|
||||
EXPECT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
|
||||
EXPECT_FALSE(static_cast<bool>(builder.Build()));
|
||||
|
||||
// Succeeds if there is sufficient input
|
||||
builder.AddTransparentInput(COutPoint(), scriptPubKey, 1);
|
||||
EXPECT_TRUE(static_cast<bool>(builder.Build()));
|
||||
|
||||
// Revert to default
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
|
||||
}
|
||||
|
||||
TEST(TransactionBuilder, ChangeOutput)
|
||||
{
|
||||
SelectParams(CBaseChainParams::REGTEST);
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
|
||||
auto consensusParams = Params().GetConsensus();
|
||||
|
||||
// Generate dummy Sapling address
|
||||
auto sk = libzcash::SaplingSpendingKey::random();
|
||||
auto expsk = sk.expanded_spending_key();
|
||||
auto pk = sk.default_address();
|
||||
|
||||
// Generate dummy Sapling note
|
||||
libzcash::SaplingNote note(pk, 25000);
|
||||
auto cm = note.cm().value();
|
||||
SaplingMerkleTree tree;
|
||||
tree.append(cm);
|
||||
auto anchor = tree.root();
|
||||
auto witness = tree.witness();
|
||||
|
||||
// Generate change Sapling address
|
||||
auto sk2 = libzcash::SaplingSpendingKey::random();
|
||||
auto fvkOut = sk2.full_viewing_key();
|
||||
auto zChangeAddr = sk2.default_address();
|
||||
|
||||
// Set up dummy transparent address
|
||||
CBasicKeyStore keystore;
|
||||
CKey tsk = DecodeSecret(tSecretRegtest);
|
||||
keystore.AddKey(tsk);
|
||||
auto tkeyid = tsk.GetPubKey().GetID();
|
||||
auto scriptPubKey = GetScriptForDestination(tkeyid);
|
||||
CTxDestination taddr = tkeyid;
|
||||
|
||||
// No change address and no Sapling spends
|
||||
{
|
||||
auto builder = TransactionBuilder(consensusParams, 1, &keystore);
|
||||
builder.AddTransparentInput(COutPoint(), scriptPubKey, 25000);
|
||||
EXPECT_FALSE(static_cast<bool>(builder.Build()));
|
||||
}
|
||||
|
||||
// Change to the same address as the first Sapling spend
|
||||
{
|
||||
auto builder = TransactionBuilder(consensusParams, 1, &keystore);
|
||||
builder.AddTransparentInput(COutPoint(), scriptPubKey, 25000);
|
||||
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
|
||||
auto maybe_tx = builder.Build();
|
||||
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
||||
auto tx = maybe_tx.get();
|
||||
|
||||
EXPECT_EQ(tx.vin.size(), 1);
|
||||
EXPECT_EQ(tx.vout.size(), 0);
|
||||
EXPECT_EQ(tx.vjoinsplit.size(), 0);
|
||||
EXPECT_EQ(tx.vShieldedSpend.size(), 1);
|
||||
EXPECT_EQ(tx.vShieldedOutput.size(), 1);
|
||||
EXPECT_EQ(tx.valueBalance, -15000);
|
||||
}
|
||||
|
||||
// Change to a Sapling address
|
||||
{
|
||||
auto builder = TransactionBuilder(consensusParams, 1, &keystore);
|
||||
builder.AddTransparentInput(COutPoint(), scriptPubKey, 25000);
|
||||
builder.SendChangeTo(zChangeAddr, fvkOut);
|
||||
auto maybe_tx = builder.Build();
|
||||
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
||||
auto tx = maybe_tx.get();
|
||||
|
||||
EXPECT_EQ(tx.vin.size(), 1);
|
||||
EXPECT_EQ(tx.vout.size(), 0);
|
||||
EXPECT_EQ(tx.vjoinsplit.size(), 0);
|
||||
EXPECT_EQ(tx.vShieldedSpend.size(), 0);
|
||||
EXPECT_EQ(tx.vShieldedOutput.size(), 1);
|
||||
EXPECT_EQ(tx.valueBalance, -15000);
|
||||
}
|
||||
|
||||
// Change to a transparent address
|
||||
{
|
||||
auto builder = TransactionBuilder(consensusParams, 1, &keystore);
|
||||
builder.AddTransparentInput(COutPoint(), scriptPubKey, 25000);
|
||||
ASSERT_TRUE(builder.SendChangeTo(taddr));
|
||||
auto maybe_tx = builder.Build();
|
||||
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
||||
auto tx = maybe_tx.get();
|
||||
|
||||
EXPECT_EQ(tx.vin.size(), 1);
|
||||
EXPECT_EQ(tx.vout.size(), 1);
|
||||
EXPECT_EQ(tx.vjoinsplit.size(), 0);
|
||||
EXPECT_EQ(tx.vShieldedSpend.size(), 0);
|
||||
EXPECT_EQ(tx.vShieldedOutput.size(), 0);
|
||||
EXPECT_EQ(tx.valueBalance, 0);
|
||||
EXPECT_EQ(tx.vout[0].nValue, 15000);
|
||||
}
|
||||
|
||||
// Revert to default
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
|
||||
}
|
||||
|
||||
TEST(TransactionBuilder, SetFee)
|
||||
{
|
||||
SelectParams(CBaseChainParams::REGTEST);
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
|
||||
auto consensusParams = Params().GetConsensus();
|
||||
|
||||
// Generate dummy Sapling address
|
||||
auto sk = libzcash::SaplingSpendingKey::random();
|
||||
auto expsk = sk.expanded_spending_key();
|
||||
auto fvk = sk.full_viewing_key();
|
||||
auto pk = sk.default_address();
|
||||
|
||||
// Generate dummy Sapling note
|
||||
libzcash::SaplingNote note(pk, 50000);
|
||||
auto cm = note.cm().value();
|
||||
SaplingMerkleTree tree;
|
||||
tree.append(cm);
|
||||
auto anchor = tree.root();
|
||||
auto witness = tree.witness();
|
||||
|
||||
// Default fee
|
||||
{
|
||||
auto builder = TransactionBuilder(consensusParams, 1);
|
||||
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
|
||||
builder.AddSaplingOutput(fvk, pk, 25000, {});
|
||||
auto maybe_tx = builder.Build();
|
||||
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
||||
auto tx = maybe_tx.get();
|
||||
|
||||
EXPECT_EQ(tx.vin.size(), 0);
|
||||
EXPECT_EQ(tx.vout.size(), 0);
|
||||
EXPECT_EQ(tx.vjoinsplit.size(), 0);
|
||||
EXPECT_EQ(tx.vShieldedSpend.size(), 1);
|
||||
EXPECT_EQ(tx.vShieldedOutput.size(), 2);
|
||||
EXPECT_EQ(tx.valueBalance, 10000);
|
||||
}
|
||||
|
||||
// Configured fee
|
||||
{
|
||||
auto builder = TransactionBuilder(consensusParams, 1);
|
||||
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
|
||||
builder.AddSaplingOutput(fvk, pk, 25000, {});
|
||||
builder.SetFee(20000);
|
||||
auto maybe_tx = builder.Build();
|
||||
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
||||
auto tx = maybe_tx.get();
|
||||
|
||||
EXPECT_EQ(tx.vin.size(), 0);
|
||||
EXPECT_EQ(tx.vout.size(), 0);
|
||||
EXPECT_EQ(tx.vjoinsplit.size(), 0);
|
||||
EXPECT_EQ(tx.vShieldedSpend.size(), 1);
|
||||
EXPECT_EQ(tx.vShieldedOutput.size(), 2);
|
||||
EXPECT_EQ(tx.valueBalance, 20000);
|
||||
}
|
||||
|
||||
// Revert to default
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
|
||||
}
|
||||
@@ -21,11 +21,15 @@ class FakeCoinsViewDB : public CCoinsView {
|
||||
public:
|
||||
FakeCoinsViewDB() {}
|
||||
|
||||
bool GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const {
|
||||
bool GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetNullifier(const uint256 &nf) const {
|
||||
bool GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetNullifier(const uint256 &nf, ShieldedType type) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -42,16 +46,19 @@ public:
|
||||
return a;
|
||||
}
|
||||
|
||||
uint256 GetBestAnchor() const {
|
||||
uint256 GetBestAnchor(ShieldedType type) const {
|
||||
uint256 a;
|
||||
return a;
|
||||
}
|
||||
|
||||
bool BatchWrite(CCoinsMap &mapCoins,
|
||||
const uint256 &hashBlock,
|
||||
const uint256 &hashAnchor,
|
||||
CAnchorsMap &mapAnchors,
|
||||
CNullifiersMap &mapNullifiers) {
|
||||
const uint256 &hashSproutAnchor,
|
||||
const uint256 &hashSaplingAnchor,
|
||||
CAnchorsSproutMap &mapSproutAnchors,
|
||||
CAnchorsSaplingMap &mapSaplingAnchors,
|
||||
CNullifiersMap &mapSproutNullifiers,
|
||||
CNullifiersMap saplingNullifiersMap) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -71,14 +78,16 @@ TEST(Validation, ContextualCheckInputsPassesWithCoinbase) {
|
||||
FakeCoinsViewDB fakeDB;
|
||||
CCoinsViewCache view(&fakeDB);
|
||||
|
||||
auto consensusBranchId = SPROUT_BRANCH_ID;
|
||||
CValidationState state;
|
||||
PrecomputedTransactionData txdata(tx);
|
||||
EXPECT_TRUE(ContextualCheckInputs(tx, state, view, false, 0, false, txdata, Params(CBaseChainParams::MAIN).GetConsensus(), consensusBranchId));
|
||||
for (int idx = Consensus::BASE_SPROUT; idx < Consensus::MAX_NETWORK_UPGRADES; idx++) {
|
||||
auto consensusBranchId = NetworkUpgradeInfo[idx].nBranchId;
|
||||
CValidationState state;
|
||||
PrecomputedTransactionData txdata(tx);
|
||||
EXPECT_TRUE(ContextualCheckInputs(tx, state, view, false, 0, false, txdata, Params(CBaseChainParams::MAIN).GetConsensus(), consensusBranchId));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Validation, ReceivedBlockTransactions) {
|
||||
auto sk = libzcash::SpendingKey::random();
|
||||
auto sk = libzcash::SproutSpendingKey::random();
|
||||
|
||||
// Create a fake genesis block
|
||||
CBlock block1;
|
||||
|
||||
134
src/gtest/test_zip32.cpp
Normal file
134
src/gtest/test_zip32.cpp
Normal file
@@ -0,0 +1,134 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
#include <zcash/zip32.h>
|
||||
|
||||
// From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_zip32.py
|
||||
// Sapling consistently uses little-endian encoding, but uint256S takes its input in
|
||||
// big-endian byte order, so the test vectors below are byte-reversed.
|
||||
TEST(ZIP32, TestVectors) {
|
||||
std::vector<unsigned char, secure_allocator<unsigned char>> rawSeed {
|
||||
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};
|
||||
HDSeed seed(rawSeed);
|
||||
|
||||
auto m = libzcash::SaplingExtendedSpendingKey::Master(seed);
|
||||
EXPECT_EQ(m.depth, 0);
|
||||
EXPECT_EQ(m.parentFVKTag, 0);
|
||||
EXPECT_EQ(m.childIndex, 0);
|
||||
EXPECT_EQ(
|
||||
m.chaincode,
|
||||
uint256S("8e661820750d557e8b34733ebf7ecdfdf31c6d27724fb47aa372bf034b7c94d0"));
|
||||
EXPECT_EQ(
|
||||
m.expsk.ask,
|
||||
uint256S("06257454c907f6510ba1c1830ebf60657760a8869ee968a2b93260d3930cc0b6"));
|
||||
EXPECT_EQ(
|
||||
m.expsk.nsk,
|
||||
uint256S("06ea21888a749fd38eb443d20a030abd2e6e997f5db4f984bd1f2f3be8ed0482"));
|
||||
EXPECT_EQ(
|
||||
m.expsk.ovk,
|
||||
uint256S("21fb4adfa42183848306ffb27719f27d76cf9bb81d023c93d4b9230389845839"));
|
||||
EXPECT_EQ(
|
||||
m.dk,
|
||||
uint256S("72a196f93e8abc0935280ea2a96fa57d6024c9913e0f9fb3af96775bb77cc177"));
|
||||
EXPECT_THAT(
|
||||
m.ToXFVK().DefaultAddress().d,
|
||||
testing::ElementsAreArray({ 0xd8, 0x62, 0x1b, 0x98, 0x1c, 0xf3, 0x00, 0xe9, 0xd4, 0xcc, 0x89 }));
|
||||
|
||||
auto m_1 = m.Derive(1);
|
||||
EXPECT_EQ(m_1.depth, 1);
|
||||
EXPECT_EQ(m_1.parentFVKTag, 0x3a71c214);
|
||||
EXPECT_EQ(m_1.childIndex, 1);
|
||||
EXPECT_EQ(
|
||||
m_1.chaincode,
|
||||
uint256S("e6bcda05678a43fad229334ef0b795a590e7c50590baf0d9b9031a690c114701"));
|
||||
EXPECT_EQ(
|
||||
m_1.expsk.ask,
|
||||
uint256S("0c357a2655b4b8d761794095df5cb402d3ba4a428cf6a88e7c2816a597c12b28"));
|
||||
EXPECT_EQ(
|
||||
m_1.expsk.nsk,
|
||||
uint256S("01ba6bff1018fd4eac04da7e3f2c6be9c229e662c5c4d1d6fc1ecafd8829a3e7"));
|
||||
EXPECT_EQ(
|
||||
m_1.expsk.ovk,
|
||||
uint256S("7474a4c518551bd82f14a7f7365a8ffa403c50cfeffedf026ada8688fc81135f"));
|
||||
EXPECT_EQ(
|
||||
m_1.dk,
|
||||
uint256S("dcb4c170d878510e96c4a74192d7eecde9c9912b00b99a12ec91d7a232e84de0"));
|
||||
EXPECT_THAT(
|
||||
m_1.ToXFVK().DefaultAddress().d,
|
||||
testing::ElementsAreArray({ 0x8b, 0x41, 0x38, 0x32, 0x0d, 0xfa, 0xfd, 0x7b, 0x39, 0x97, 0x81 }));
|
||||
|
||||
auto m_1_2h = m_1.Derive(2 | ZIP32_HARDENED_KEY_LIMIT);
|
||||
EXPECT_EQ(m_1_2h.depth, 2);
|
||||
EXPECT_EQ(m_1_2h.parentFVKTag, 0x079e99db);
|
||||
EXPECT_EQ(m_1_2h.childIndex, 2 | ZIP32_HARDENED_KEY_LIMIT);
|
||||
EXPECT_EQ(
|
||||
m_1_2h.chaincode,
|
||||
uint256S("35d4a883737742ca41a4baa92323bdb3c93dcb3b462a26b039971bedf415ce97"));
|
||||
EXPECT_EQ(
|
||||
m_1_2h.expsk.ask,
|
||||
uint256S("0dc6e4fe846bda925c82e632980434e17b51dac81fc4821fa71334ee3c11e88b"));
|
||||
EXPECT_EQ(
|
||||
m_1_2h.expsk.nsk,
|
||||
uint256S("0c99a63a275c1c66734761cfb9c62fe9bd1b953f579123d3d0e769c59d057837"));
|
||||
EXPECT_EQ(
|
||||
m_1_2h.expsk.ovk,
|
||||
uint256S("bc1328fc5eb693e18875c5149d06953b11d39447ebd6e38c023c22962e1881cf"));
|
||||
EXPECT_EQ(
|
||||
m_1_2h.dk,
|
||||
uint256S("377bb062dce7e0dcd8a0054d0ca4b4d1481b3710bfa1df12ca46ff9e9fa1eda3"));
|
||||
EXPECT_THAT(
|
||||
m_1_2h.ToXFVK().DefaultAddress().d,
|
||||
testing::ElementsAreArray({ 0xe8, 0xd0, 0x37, 0x93, 0xcd, 0xd2, 0xba, 0xcc, 0x9c, 0x70, 0x41 }));
|
||||
|
||||
auto m_1_2hv = m_1_2h.ToXFVK();
|
||||
EXPECT_EQ(m_1_2hv.depth, 2);
|
||||
EXPECT_EQ(m_1_2hv.parentFVKTag, 0x079e99db);
|
||||
EXPECT_EQ(m_1_2hv.childIndex, 2 | ZIP32_HARDENED_KEY_LIMIT);
|
||||
EXPECT_EQ(
|
||||
m_1_2hv.chaincode,
|
||||
uint256S("35d4a883737742ca41a4baa92323bdb3c93dcb3b462a26b039971bedf415ce97"));
|
||||
EXPECT_EQ(
|
||||
m_1_2hv.fvk.ak,
|
||||
uint256S("4138cffdf7200e52d4e9f4384481b4a4c4d070493a5e401e4ffa850f5a92c5a6"));
|
||||
EXPECT_EQ(
|
||||
m_1_2hv.fvk.nk,
|
||||
uint256S("11eee22577304f660cc036bc84b3fc88d1ec50ae8a4d657beb6b211659304e30"));
|
||||
EXPECT_EQ(
|
||||
m_1_2hv.fvk.ovk,
|
||||
uint256S("bc1328fc5eb693e18875c5149d06953b11d39447ebd6e38c023c22962e1881cf"));
|
||||
EXPECT_EQ(
|
||||
m_1_2hv.dk,
|
||||
uint256S("377bb062dce7e0dcd8a0054d0ca4b4d1481b3710bfa1df12ca46ff9e9fa1eda3"));
|
||||
EXPECT_EQ(m_1_2hv.DefaultAddress(), m_1_2h.ToXFVK().DefaultAddress());
|
||||
|
||||
// Hardened derivation from an xfvk fails
|
||||
EXPECT_FALSE(m_1_2hv.Derive(3 | ZIP32_HARDENED_KEY_LIMIT));
|
||||
|
||||
// Non-hardened derivation succeeds
|
||||
auto maybe_m_1_2hv_3 = m_1_2hv.Derive(3);
|
||||
EXPECT_TRUE(maybe_m_1_2hv_3);
|
||||
|
||||
auto m_1_2hv_3 = maybe_m_1_2hv_3.get();
|
||||
EXPECT_EQ(m_1_2hv_3.depth, 3);
|
||||
EXPECT_EQ(m_1_2hv_3.parentFVKTag, 0x7583c148);
|
||||
EXPECT_EQ(m_1_2hv_3.childIndex, 3);
|
||||
EXPECT_EQ(
|
||||
m_1_2hv_3.chaincode,
|
||||
uint256S("e8e7d6a74a5a1c05be41baec7998d91f7b3603a4c0af495b0d43ba81cf7b938d"));
|
||||
EXPECT_EQ(
|
||||
m_1_2hv_3.fvk.ak,
|
||||
uint256S("a3a697bdda9d648d32a97553de4754b2fac866d726d3f2c436259c507bc585b1"));
|
||||
EXPECT_EQ(
|
||||
m_1_2hv_3.fvk.nk,
|
||||
uint256S("4f66c0814b769963f3bf1bc001270b50edabb27de042fc8a5607d2029e0488db"));
|
||||
EXPECT_EQ(
|
||||
m_1_2hv_3.fvk.ovk,
|
||||
uint256S("f61a699934dc78441324ef628b4b4721611571e8ee3bd591eb3d4b1cfae0b969"));
|
||||
EXPECT_EQ(
|
||||
m_1_2hv_3.dk,
|
||||
uint256S("6ee53b1261f2c9c0f7359ab236f87b52a0f1b0ce43305cdad92ebb63c350cbbe"));
|
||||
EXPECT_THAT(
|
||||
m_1_2hv_3.DefaultAddress().d,
|
||||
testing::ElementsAreArray({ 0x03, 0x0f, 0xfb, 0x26, 0x3a, 0x93, 0x9e, 0x23, 0x0e, 0x96, 0xdd }));
|
||||
}
|
||||
29
src/hash.h
29
src/hash.h
@@ -9,6 +9,7 @@
|
||||
#include "crypto/ripemd160.h"
|
||||
#include "crypto/sha256.h"
|
||||
#include "crypto/verus_hash.h"
|
||||
#include "prevector.h"
|
||||
#include "serialize.h"
|
||||
#include "uint256.h"
|
||||
#include "version.h"
|
||||
@@ -121,21 +122,30 @@ inline uint160 Hash160(const std::vector<unsigned char>& vch)
|
||||
return Hash160(vch.begin(), vch.end());
|
||||
}
|
||||
|
||||
/** Compute the 160-bit hash of a vector. */
|
||||
template<unsigned int N>
|
||||
inline uint160 Hash160(const prevector<N, unsigned char>& vch)
|
||||
{
|
||||
return Hash160(vch.begin(), vch.end());
|
||||
}
|
||||
|
||||
/** A writer stream (for serialization) that computes a 256-bit hash. */
|
||||
class CHashWriter
|
||||
{
|
||||
private:
|
||||
CHash256 ctx;
|
||||
|
||||
const int nType;
|
||||
const int nVersion;
|
||||
public:
|
||||
int nType;
|
||||
int nVersion;
|
||||
|
||||
CHashWriter(int nTypeIn, int nVersionIn) : nType(nTypeIn), nVersion(nVersionIn) {}
|
||||
|
||||
CHashWriter& write(const char *pch, size_t size) {
|
||||
int GetType() const { return nType; }
|
||||
int GetVersion() const { return nVersion; }
|
||||
|
||||
void write(const char *pch, size_t size) {
|
||||
ctx.Write((const unsigned char*)pch, size);
|
||||
return (*this);
|
||||
}
|
||||
|
||||
// invalidates the object
|
||||
@@ -148,7 +158,7 @@ public:
|
||||
template<typename T>
|
||||
CHashWriter& operator<<(const T& obj) {
|
||||
// Serialize to this stream
|
||||
::Serialize(*this, obj, nType, nVersion);
|
||||
::Serialize(*this, obj);
|
||||
return (*this);
|
||||
}
|
||||
};
|
||||
@@ -173,6 +183,9 @@ public:
|
||||
personal) == 0);
|
||||
}
|
||||
|
||||
int GetType() const { return nType; }
|
||||
int GetVersion() const { return nVersion; }
|
||||
|
||||
CBLAKE2bWriter& write(const char *pch, size_t size) {
|
||||
crypto_generichash_blake2b_update(&state, (const unsigned char*)pch, size);
|
||||
return (*this);
|
||||
@@ -188,7 +201,7 @@ public:
|
||||
template<typename T>
|
||||
CBLAKE2bWriter& operator<<(const T& obj) {
|
||||
// Serialize to this stream
|
||||
::Serialize(*this, obj, nType, nVersion);
|
||||
::Serialize(*this, obj);
|
||||
return (*this);
|
||||
}
|
||||
};
|
||||
@@ -224,7 +237,7 @@ public:
|
||||
template<typename T>
|
||||
CVerusHashWriter& operator<<(const T& obj) {
|
||||
// Serialize to this stream
|
||||
::Serialize(*this, obj, nType, nVersion);
|
||||
::Serialize(*this, obj);
|
||||
return (*this);
|
||||
}
|
||||
};
|
||||
@@ -260,7 +273,7 @@ public:
|
||||
template<typename T>
|
||||
CVerusHashV2Writer& operator<<(const T& obj) {
|
||||
// Serialize to this stream
|
||||
::Serialize(*this, obj, nType, nVersion);
|
||||
::Serialize(*this, obj);
|
||||
return (*this);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#include "httprpc.h"
|
||||
|
||||
#include "base58.h"
|
||||
#include "chainparams.h"
|
||||
#include "httpserver.h"
|
||||
#include "rpcprotocol.h"
|
||||
#include "rpcserver.h"
|
||||
#include "key_io.h"
|
||||
#include "rpc/protocol.h"
|
||||
#include "rpc/server.h"
|
||||
#include "random.h"
|
||||
#include "sync.h"
|
||||
#include "util.h"
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include "compat.h"
|
||||
#include "util.h"
|
||||
#include "netbase.h"
|
||||
#include "rpcprotocol.h" // For HTTP status codes
|
||||
#include "rpc/protocol.h" // For HTTP status codes
|
||||
#include "sync.h"
|
||||
#include "ui_interface.h"
|
||||
|
||||
|
||||
103
src/init.cpp
103
src/init.cpp
@@ -12,9 +12,6 @@
|
||||
#include "primitives/block.h"
|
||||
#include "addrman.h"
|
||||
#include "amount.h"
|
||||
#ifdef ENABLE_MINING
|
||||
#include "base58.h"
|
||||
#endif
|
||||
#include "checkpoints.h"
|
||||
#include "compat/sanity.h"
|
||||
#include "consensus/upgrades.h"
|
||||
@@ -23,11 +20,15 @@
|
||||
#include "httprpc.h"
|
||||
#include "key.h"
|
||||
#include "notarisationdb.h"
|
||||
#ifdef ENABLE_MINING
|
||||
#include "key_io.h"
|
||||
#endif
|
||||
#include "main.h"
|
||||
#include "metrics.h"
|
||||
#include "miner.h"
|
||||
#include "net.h"
|
||||
#include "rpcserver.h"
|
||||
#include "rpc/server.h"
|
||||
#include "rpc/register.h"
|
||||
#include "script/standard.h"
|
||||
#include "scheduler.h"
|
||||
#include "txdb.h"
|
||||
@@ -68,6 +69,8 @@
|
||||
#include "amqp/amqpnotificationinterface.h"
|
||||
#endif
|
||||
|
||||
#include "librustzcash.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
extern void ThreadSendAlert();
|
||||
@@ -352,13 +355,11 @@ std::string HelpMessage(HelpMessageMode mode)
|
||||
#endif
|
||||
}
|
||||
strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory"));
|
||||
strUsage += HelpMessageOpt("-disabledeprecation=<version>", strprintf(_("Disable block-height node deprecation and automatic shutdown (example: -disabledeprecation=%s)"),
|
||||
FormatVersion(CLIENT_VERSION)));
|
||||
strUsage += HelpMessageOpt("-exportdir=<dir>", _("Specify directory to be used when exporting data"));
|
||||
strUsage += HelpMessageOpt("-dbcache=<n>", strprintf(_("Set database cache size in megabytes (%d to %d, default: %d)"), nMinDbCache, nMaxDbCache, nDefaultDbCache));
|
||||
strUsage += HelpMessageOpt("-loadblock=<file>", _("Imports blocks from external blk000??.dat file") + " " + _("on startup"));
|
||||
strUsage += HelpMessageOpt("-maxorphantx=<n>", strprintf(_("Keep at most <n> unconnectable transactions in memory (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS));
|
||||
strUsage += HelpMessageOpt("-mempooltxinputlimit=<n>", _("Set the maximum number of transparent inputs in a transaction that the mempool will accept (default: 0 = no limit applied)"));
|
||||
strUsage += HelpMessageOpt("-mempooltxinputlimit=<n>", _("[DEPRECATED FROM OVERWINTER] Set the maximum number of transparent inputs in a transaction that the mempool will accept (default: 0 = no limit applied)"));
|
||||
strUsage += HelpMessageOpt("-par=<n>", strprintf(_("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)"),
|
||||
-(int)boost::thread::hardware_concurrency(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS));
|
||||
#ifndef _WIN32
|
||||
@@ -394,6 +395,9 @@ std::string HelpMessage(HelpMessageMode mode)
|
||||
strUsage += HelpMessageOpt("-onion=<ip:port>", strprintf(_("Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: %s)"), "-proxy"));
|
||||
strUsage += HelpMessageOpt("-onlynet=<net>", _("Only connect to nodes in network <net> (ipv4, ipv6 or onion)"));
|
||||
strUsage += HelpMessageOpt("-permitbaremultisig", strprintf(_("Relay non-P2SH multisig (default: %u)"), 1));
|
||||
strUsage += HelpMessageOpt("-peerbloomfilters", strprintf(_("Support filtering of blocks and transaction with Bloom filters (default: %u)"), 1));
|
||||
if (showDebug)
|
||||
strUsage += HelpMessageOpt("-enforcenodebloom", strprintf("Enforce minimum protocol version to limit use of Bloom filters (default: %u)", 0));
|
||||
strUsage += HelpMessageOpt("-port=<port>", strprintf(_("Listen for connections on <port> (default: %u or testnet: %u)"), 7770, 17770));
|
||||
strUsage += HelpMessageOpt("-proxy=<ip:port>", _("Connect through SOCKS5 proxy"));
|
||||
strUsage += HelpMessageOpt("-proxyrandomize", strprintf(_("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)"), 1));
|
||||
@@ -472,6 +476,7 @@ std::string HelpMessage(HelpMessageMode mode)
|
||||
strUsage += HelpMessageOpt("-limitfreerelay=<n>", strprintf("Continuously rate-limit free transactions to <n>*1000 bytes per minute (default: %u)", 15));
|
||||
strUsage += HelpMessageOpt("-relaypriority", strprintf("Require high priority for relaying free or low-fee transactions (default: %u)", 0));
|
||||
strUsage += HelpMessageOpt("-maxsigcachesize=<n>", strprintf("Limit size of signature cache to <n> entries (default: %u)", 50000));
|
||||
strUsage += HelpMessageOpt("-maxtipage=<n>", strprintf("Maximum tip age in seconds to consider node in initial block download (default: %u)", DEFAULT_MAX_TIP_AGE));
|
||||
}
|
||||
strUsage += HelpMessageOpt("-minrelaytxfee=<amt>", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for relaying (default: %s)"),
|
||||
CURRENCY_UNIT, FormatMoney(::minRelayTxFee.GetFeePerK())));
|
||||
@@ -683,15 +688,26 @@ bool InitSanityCheck(void)
|
||||
}
|
||||
|
||||
|
||||
static void ZC_LoadParams()
|
||||
static void ZC_LoadParams(
|
||||
const CChainParams& chainparams
|
||||
)
|
||||
{
|
||||
struct timeval tv_start, tv_end;
|
||||
float elapsed;
|
||||
|
||||
boost::filesystem::path pk_path = ZC_GetParamsDir() / "sprout-proving.key";
|
||||
boost::filesystem::path vk_path = ZC_GetParamsDir() / "sprout-verifying.key";
|
||||
boost::filesystem::path sapling_spend = ZC_GetParamsDir() / "sapling-spend.params";
|
||||
boost::filesystem::path sapling_output = ZC_GetParamsDir() / "sapling-output.params";
|
||||
boost::filesystem::path sprout_groth16 = ZC_GetParamsDir() / "sprout-groth16.params";
|
||||
|
||||
if (!(boost::filesystem::exists(pk_path) && boost::filesystem::exists(vk_path))) {
|
||||
if (!(
|
||||
boost::filesystem::exists(pk_path) &&
|
||||
boost::filesystem::exists(vk_path) &&
|
||||
boost::filesystem::exists(sapling_spend) &&
|
||||
boost::filesystem::exists(sapling_output) &&
|
||||
boost::filesystem::exists(sprout_groth16)
|
||||
)) {
|
||||
uiInterface.ThreadSafeMessageBox(strprintf(
|
||||
_("Cannot find the Zcash network parameters in the following directory:\n"
|
||||
"%s\n"
|
||||
@@ -710,6 +726,28 @@ static void ZC_LoadParams()
|
||||
gettimeofday(&tv_end, 0);
|
||||
elapsed = float(tv_end.tv_sec-tv_start.tv_sec) + (tv_end.tv_usec-tv_start.tv_usec)/float(1000000);
|
||||
LogPrintf("Loaded verifying key in %fs seconds.\n", elapsed);
|
||||
|
||||
std::string sapling_spend_str = sapling_spend.string();
|
||||
std::string sapling_output_str = sapling_output.string();
|
||||
std::string sprout_groth16_str = sprout_groth16.string();
|
||||
|
||||
LogPrintf("Loading Sapling (Spend) parameters from %s\n", sapling_spend_str.c_str());
|
||||
LogPrintf("Loading Sapling (Output) parameters from %s\n", sapling_output_str.c_str());
|
||||
LogPrintf("Loading Sapling (Sprout Groth16) parameters from %s\n", sprout_groth16_str.c_str());
|
||||
gettimeofday(&tv_start, 0);
|
||||
|
||||
librustzcash_init_zksnark_params(
|
||||
sapling_spend_str.c_str(),
|
||||
"8270785a1a0d0bc77196f000ee6d221c9c9894f55307bd9357c3f0105d31ca63991ab91324160d8f53e2bbd3c2633a6eb8bdf5205d822e7f3f73edac51b2b70c",
|
||||
sapling_output_str.c_str(),
|
||||
"657e3d38dbb5cb5e7dd2970e8b03d69b4787dd907285b5a7f0790dcc8072f60bf593b32cc2d1c030e00ff5ae64bf84c5c3beb84ddc841d48264b4a171744d028",
|
||||
sprout_groth16_str.c_str(),
|
||||
"e9b238411bd6c0ec4791e9d04245ec350c9c5744f5610dfcce4365d5ca49dfefd5054e371842b3f88fa1b9d7e8e075249b3ebabd167fa8b0f3161292d36c180a"
|
||||
);
|
||||
|
||||
gettimeofday(&tv_end, 0);
|
||||
elapsed = float(tv_end.tv_sec-tv_start.tv_sec) + (tv_end.tv_usec-tv_start.tv_usec)/float(1000000);
|
||||
LogPrintf("Loaded Sapling parameters in %fs seconds.\n", elapsed);
|
||||
}
|
||||
|
||||
bool AppInitServers(boost::thread_group& threadGroup)
|
||||
@@ -792,6 +830,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
|
||||
std::set_new_handler(new_handler_terminate);
|
||||
|
||||
// ********************************************************* Step 2: parameter interactions
|
||||
const CChainParams& chainparams = Params();
|
||||
|
||||
@@ -960,8 +1000,11 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
fPruneMode = true;
|
||||
}
|
||||
|
||||
RegisterAllCoreRPCCommands(tableRPC);
|
||||
#ifdef ENABLE_WALLET
|
||||
bool fDisableWallet = GetBoolArg("-disablewallet", false);
|
||||
if (!fDisableWallet)
|
||||
RegisterWalletRPCCommands(tableRPC);
|
||||
#endif
|
||||
|
||||
nConnectTimeout = GetArg("-timeout", DEFAULT_CONNECT_TIMEOUT);
|
||||
@@ -1036,10 +1079,15 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
// Option to startup with mocktime set (used for regression testing):
|
||||
SetMockTime(GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op
|
||||
|
||||
if (GetBoolArg("-peerbloomfilters", true))
|
||||
nLocalServices |= NODE_BLOOM;
|
||||
|
||||
nMaxTipAge = GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE);
|
||||
|
||||
#ifdef ENABLE_MINING
|
||||
if (mapArgs.count("-mineraddress")) {
|
||||
CBitcoinAddress addr;
|
||||
if (!addr.SetString(mapArgs["-mineraddress"])) {
|
||||
CTxDestination addr = DecodeDestination(mapArgs["-mineraddress"]);
|
||||
if (!IsValidDestination(addr)) {
|
||||
return InitError(strprintf(
|
||||
_("Invalid address for -mineraddress=<addr>: '%s' (must be a transparent address)"),
|
||||
mapArgs["-mineraddress"]));
|
||||
@@ -1183,7 +1231,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
libsnark::inhibit_profiling_counters = true;
|
||||
|
||||
// Initialize Zcash circuit parameters
|
||||
ZC_LoadParams();
|
||||
ZC_LoadParams(chainparams);
|
||||
|
||||
/* Start the RPC server already. It will be started in "warmup" mode
|
||||
* and not really process calls already (but it will signify connections
|
||||
@@ -1222,6 +1270,20 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
|
||||
RegisterNodeSignals(GetNodeSignals());
|
||||
|
||||
// sanitize comments per BIP-0014, format user agent and check total size
|
||||
std::vector<string> uacomments;
|
||||
BOOST_FOREACH(string cmt, mapMultiArgs["-uacomment"])
|
||||
{
|
||||
if (cmt != SanitizeString(cmt, SAFE_CHARS_UA_COMMENT))
|
||||
return InitError(strprintf("User Agent comment (%s) contains unsafe characters.", cmt));
|
||||
uacomments.push_back(SanitizeString(cmt, SAFE_CHARS_UA_COMMENT));
|
||||
}
|
||||
strSubVersion = FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, uacomments);
|
||||
if (strSubVersion.size() > MAX_SUBVERSION_LENGTH) {
|
||||
return InitError(strprintf("Total length of network version string %i exceeds maximum of %i characters. Reduce the number and/or size of uacomments.",
|
||||
strSubVersion.size(), MAX_SUBVERSION_LENGTH));
|
||||
}
|
||||
|
||||
if (mapArgs.count("-onlynet")) {
|
||||
std::set<enum Network> nets;
|
||||
BOOST_FOREACH(const std::string& snet, mapMultiArgs["-onlynet"]) {
|
||||
@@ -1432,6 +1494,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
}
|
||||
}
|
||||
|
||||
bool clearWitnessCaches = false;
|
||||
|
||||
bool fLoaded = false;
|
||||
while (!fLoaded) {
|
||||
bool fReset = fReindex;
|
||||
@@ -1494,7 +1558,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
|
||||
if (!fReindex) {
|
||||
uiInterface.InitMessage(_("Rewinding blocks if needed..."));
|
||||
if (!RewindBlockIndex(chainparams)) {
|
||||
if (!RewindBlockIndex(chainparams, clearWitnessCaches)) {
|
||||
strLoadError = _("Unable to rewind the database to a pre-upgrade state. You will need to redownload the blockchain");
|
||||
break;
|
||||
}
|
||||
@@ -1628,6 +1692,12 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
pwalletMain->SetMaxVersion(nMaxVersion);
|
||||
}
|
||||
|
||||
if (!pwalletMain->HaveHDSeed())
|
||||
{
|
||||
// generate a new HD seed
|
||||
pwalletMain->GenerateNewSeed();
|
||||
}
|
||||
|
||||
if (fFirstRun)
|
||||
{
|
||||
// Create new keyUser and set as default key
|
||||
@@ -1647,7 +1717,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
RegisterValidationInterface(pwalletMain);
|
||||
|
||||
CBlockIndex *pindexRescan = chainActive.Tip();
|
||||
if (GetBoolArg("-rescan", false))
|
||||
if (clearWitnessCaches || GetBoolArg("-rescan", false))
|
||||
{
|
||||
pwalletMain->ClearNoteWitnessCache();
|
||||
pindexRescan = chainActive.Genesis();
|
||||
@@ -1717,9 +1787,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
bool minerAddressInLocalWallet = false;
|
||||
if (pwalletMain) {
|
||||
// Address has alreday been validated
|
||||
CBitcoinAddress addr(mapArgs["-mineraddress"]);
|
||||
CKeyID keyID;
|
||||
addr.GetKeyID(keyID);
|
||||
CTxDestination addr = DecodeDestination(mapArgs["-mineraddress"]);
|
||||
CKeyID keyID = boost::get<CKeyID>(addr);
|
||||
minerAddressInLocalWallet = pwalletMain->HaveKey(keyID);
|
||||
}
|
||||
if (GetBoolArg("-minetolocalwallet", true) && !minerAddressInLocalWallet) {
|
||||
|
||||
@@ -307,7 +307,7 @@ CExtPubKey CExtKey::Neuter() const {
|
||||
return ret;
|
||||
}
|
||||
|
||||
void CExtKey::Encode(unsigned char code[74]) const {
|
||||
void CExtKey::Encode(unsigned char code[BIP32_EXTKEY_SIZE]) const {
|
||||
code[0] = nDepth;
|
||||
memcpy(code+1, vchFingerprint, 4);
|
||||
code[5] = (nChild >> 24) & 0xFF; code[6] = (nChild >> 16) & 0xFF;
|
||||
@@ -318,12 +318,12 @@ void CExtKey::Encode(unsigned char code[74]) const {
|
||||
memcpy(code+42, key.begin(), 32);
|
||||
}
|
||||
|
||||
void CExtKey::Decode(const unsigned char code[74]) {
|
||||
void CExtKey::Decode(const unsigned char code[BIP32_EXTKEY_SIZE]) {
|
||||
nDepth = code[0];
|
||||
memcpy(vchFingerprint, code+1, 4);
|
||||
nChild = (code[5] << 24) | (code[6] << 16) | (code[7] << 8) | code[8];
|
||||
memcpy(chaincode.begin(), code+9, 32);
|
||||
key.Set(code+42, code+74, true);
|
||||
key.Set(code+42, code+BIP32_EXTKEY_SIZE, true);
|
||||
}
|
||||
|
||||
bool ECC_InitSanityCheck() {
|
||||
|
||||
21
src/key.h
21
src/key.h
@@ -170,11 +170,28 @@ struct CExtKey {
|
||||
a.chaincode == b.chaincode && a.key == b.key;
|
||||
}
|
||||
|
||||
void Encode(unsigned char code[74]) const;
|
||||
void Decode(const unsigned char code[74]);
|
||||
void Encode(unsigned char code[BIP32_EXTKEY_SIZE]) const;
|
||||
void Decode(const unsigned char code[BIP32_EXTKEY_SIZE]);
|
||||
bool Derive(CExtKey& out, unsigned int nChild) const;
|
||||
CExtPubKey Neuter() const;
|
||||
void SetMaster(const unsigned char* seed, unsigned int nSeedLen);
|
||||
template <typename Stream>
|
||||
void Serialize(Stream& s) const
|
||||
{
|
||||
unsigned int len = BIP32_EXTKEY_SIZE;
|
||||
::WriteCompactSize(s, len);
|
||||
unsigned char code[BIP32_EXTKEY_SIZE];
|
||||
Encode(code);
|
||||
s.write((const char *)&code[0], len);
|
||||
}
|
||||
template <typename Stream>
|
||||
void Unserialize(Stream& s)
|
||||
{
|
||||
unsigned int len = ::ReadCompactSize(s);
|
||||
unsigned char code[BIP32_EXTKEY_SIZE];
|
||||
s.read((char *)&code[0], len);
|
||||
Decode(code);
|
||||
}
|
||||
};
|
||||
|
||||
/** Initialize the elliptic curve support. May not be called twice without calling ECC_Stop first. */
|
||||
|
||||
377
src/key_io.cpp
Normal file
377
src/key_io.cpp
Normal file
@@ -0,0 +1,377 @@
|
||||
// Copyright (c) 2014-2016 The Bitcoin Core developers
|
||||
// Copyright (c) 2016-2018 The Zcash developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <key_io.h>
|
||||
|
||||
#include <base58.h>
|
||||
#include <bech32.h>
|
||||
#include <script/script.h>
|
||||
#include <utilstrencodings.h>
|
||||
|
||||
#include <boost/variant/apply_visitor.hpp>
|
||||
#include <boost/variant/static_visitor.hpp>
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
|
||||
namespace
|
||||
{
|
||||
class DestinationEncoder : public boost::static_visitor<std::string>
|
||||
{
|
||||
private:
|
||||
const CChainParams& m_params;
|
||||
|
||||
public:
|
||||
DestinationEncoder(const CChainParams& params) : m_params(params) {}
|
||||
|
||||
std::string operator()(const CKeyID& id) const
|
||||
{
|
||||
std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::PUBKEY_ADDRESS);
|
||||
data.insert(data.end(), id.begin(), id.end());
|
||||
return EncodeBase58Check(data);
|
||||
}
|
||||
|
||||
std::string operator()(const CScriptID& id) const
|
||||
{
|
||||
std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::SCRIPT_ADDRESS);
|
||||
data.insert(data.end(), id.begin(), id.end());
|
||||
return EncodeBase58Check(data);
|
||||
}
|
||||
|
||||
std::string operator()(const CNoDestination& no) const { return {}; }
|
||||
};
|
||||
|
||||
CTxDestination DecodeDestination(const std::string& str, const CChainParams& params)
|
||||
{
|
||||
std::vector<unsigned char> data;
|
||||
uint160 hash;
|
||||
if (DecodeBase58Check(str, data)) {
|
||||
// base58-encoded Bitcoin addresses.
|
||||
// Public-key-hash-addresses have version 0 (or 111 testnet).
|
||||
// The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key.
|
||||
const std::vector<unsigned char>& pubkey_prefix = params.Base58Prefix(CChainParams::PUBKEY_ADDRESS);
|
||||
if (data.size() == hash.size() + pubkey_prefix.size() && std::equal(pubkey_prefix.begin(), pubkey_prefix.end(), data.begin())) {
|
||||
std::copy(data.begin() + pubkey_prefix.size(), data.end(), hash.begin());
|
||||
return CKeyID(hash);
|
||||
}
|
||||
// Script-hash-addresses have version 5 (or 196 testnet).
|
||||
// The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script.
|
||||
const std::vector<unsigned char>& script_prefix = params.Base58Prefix(CChainParams::SCRIPT_ADDRESS);
|
||||
if (data.size() == hash.size() + script_prefix.size() && std::equal(script_prefix.begin(), script_prefix.end(), data.begin())) {
|
||||
std::copy(data.begin() + script_prefix.size(), data.end(), hash.begin());
|
||||
return CScriptID(hash);
|
||||
}
|
||||
}
|
||||
return CNoDestination();
|
||||
}
|
||||
|
||||
class PaymentAddressEncoder : public boost::static_visitor<std::string>
|
||||
{
|
||||
private:
|
||||
const CChainParams& m_params;
|
||||
|
||||
public:
|
||||
PaymentAddressEncoder(const CChainParams& params) : m_params(params) {}
|
||||
|
||||
std::string operator()(const libzcash::SproutPaymentAddress& zaddr) const
|
||||
{
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << zaddr;
|
||||
std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::ZCPAYMENT_ADDRRESS);
|
||||
data.insert(data.end(), ss.begin(), ss.end());
|
||||
return EncodeBase58Check(data);
|
||||
}
|
||||
|
||||
std::string operator()(const libzcash::SaplingPaymentAddress& zaddr) const
|
||||
{
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << zaddr;
|
||||
// ConvertBits requires unsigned char, but CDataStream uses char
|
||||
std::vector<unsigned char> seraddr(ss.begin(), ss.end());
|
||||
std::vector<unsigned char> data;
|
||||
// See calculation comment below
|
||||
data.reserve((seraddr.size() * 8 + 4) / 5);
|
||||
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, seraddr.begin(), seraddr.end());
|
||||
return bech32::Encode(m_params.Bech32HRP(CChainParams::SAPLING_PAYMENT_ADDRESS), data);
|
||||
}
|
||||
|
||||
std::string operator()(const libzcash::InvalidEncoding& no) const { return {}; }
|
||||
};
|
||||
|
||||
class ViewingKeyEncoder : public boost::static_visitor<std::string>
|
||||
{
|
||||
private:
|
||||
const CChainParams& m_params;
|
||||
|
||||
public:
|
||||
ViewingKeyEncoder(const CChainParams& params) : m_params(params) {}
|
||||
|
||||
std::string operator()(const libzcash::SproutViewingKey& vk) const
|
||||
{
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << vk;
|
||||
std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::ZCVIEWING_KEY);
|
||||
data.insert(data.end(), ss.begin(), ss.end());
|
||||
std::string ret = EncodeBase58Check(data);
|
||||
memory_cleanse(data.data(), data.size());
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string operator()(const libzcash::InvalidEncoding& no) const { return {}; }
|
||||
};
|
||||
|
||||
class SpendingKeyEncoder : public boost::static_visitor<std::string>
|
||||
{
|
||||
private:
|
||||
const CChainParams& m_params;
|
||||
|
||||
public:
|
||||
SpendingKeyEncoder(const CChainParams& params) : m_params(params) {}
|
||||
|
||||
std::string operator()(const libzcash::SproutSpendingKey& zkey) const
|
||||
{
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << zkey;
|
||||
std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::ZCSPENDING_KEY);
|
||||
data.insert(data.end(), ss.begin(), ss.end());
|
||||
std::string ret = EncodeBase58Check(data);
|
||||
memory_cleanse(data.data(), data.size());
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string operator()(const libzcash::SaplingExtendedSpendingKey& zkey) const
|
||||
{
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << zkey;
|
||||
// ConvertBits requires unsigned char, but CDataStream uses char
|
||||
std::vector<unsigned char> serkey(ss.begin(), ss.end());
|
||||
std::vector<unsigned char> data;
|
||||
// See calculation comment below
|
||||
data.reserve((serkey.size() * 8 + 4) / 5);
|
||||
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, serkey.begin(), serkey.end());
|
||||
std::string ret = bech32::Encode(m_params.Bech32HRP(CChainParams::SAPLING_EXTENDED_SPEND_KEY), data);
|
||||
memory_cleanse(serkey.data(), serkey.size());
|
||||
memory_cleanse(data.data(), data.size());
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string operator()(const libzcash::InvalidEncoding& no) const { return {}; }
|
||||
};
|
||||
|
||||
// Sizes of SaplingPaymentAddress and SaplingSpendingKey after
|
||||
// ConvertBits<8, 5, true>(). The calculations below take the
|
||||
// regular serialized size in bytes, convert to bits, and then
|
||||
// perform ceiling division to get the number of 5-bit clusters.
|
||||
const size_t ConvertedSaplingPaymentAddressSize = ((32 + 11) * 8 + 4) / 5;
|
||||
const size_t ConvertedSaplingExtendedSpendingKeySize = (ZIP32_XSK_SIZE * 8 + 4) / 5;
|
||||
} // namespace
|
||||
|
||||
CKey DecodeSecret(const std::string& str)
|
||||
{
|
||||
CKey key;
|
||||
std::vector<unsigned char> data;
|
||||
if (DecodeBase58Check(str, data)) {
|
||||
const std::vector<unsigned char>& privkey_prefix = Params().Base58Prefix(CChainParams::SECRET_KEY);
|
||||
if ((data.size() == 32 + privkey_prefix.size() || (data.size() == 33 + privkey_prefix.size() && data.back() == 1)) &&
|
||||
std::equal(privkey_prefix.begin(), privkey_prefix.end(), data.begin())) {
|
||||
bool compressed = data.size() == 33 + privkey_prefix.size();
|
||||
key.Set(data.begin() + privkey_prefix.size(), data.begin() + privkey_prefix.size() + 32, compressed);
|
||||
}
|
||||
}
|
||||
memory_cleanse(data.data(), data.size());
|
||||
return key;
|
||||
}
|
||||
|
||||
std::string EncodeSecret(const CKey& key)
|
||||
{
|
||||
assert(key.IsValid());
|
||||
std::vector<unsigned char> data = Params().Base58Prefix(CChainParams::SECRET_KEY);
|
||||
data.insert(data.end(), key.begin(), key.end());
|
||||
if (key.IsCompressed()) {
|
||||
data.push_back(1);
|
||||
}
|
||||
std::string ret = EncodeBase58Check(data);
|
||||
memory_cleanse(data.data(), data.size());
|
||||
return ret;
|
||||
}
|
||||
|
||||
CExtPubKey DecodeExtPubKey(const std::string& str)
|
||||
{
|
||||
CExtPubKey key;
|
||||
std::vector<unsigned char> data;
|
||||
if (DecodeBase58Check(str, data)) {
|
||||
const std::vector<unsigned char>& prefix = Params().Base58Prefix(CChainParams::EXT_PUBLIC_KEY);
|
||||
if (data.size() == BIP32_EXTKEY_SIZE + prefix.size() && std::equal(prefix.begin(), prefix.end(), data.begin())) {
|
||||
key.Decode(data.data() + prefix.size());
|
||||
}
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
std::string EncodeExtPubKey(const CExtPubKey& key)
|
||||
{
|
||||
std::vector<unsigned char> data = Params().Base58Prefix(CChainParams::EXT_PUBLIC_KEY);
|
||||
size_t size = data.size();
|
||||
data.resize(size + BIP32_EXTKEY_SIZE);
|
||||
key.Encode(data.data() + size);
|
||||
std::string ret = EncodeBase58Check(data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
CExtKey DecodeExtKey(const std::string& str)
|
||||
{
|
||||
CExtKey key;
|
||||
std::vector<unsigned char> data;
|
||||
if (DecodeBase58Check(str, data)) {
|
||||
const std::vector<unsigned char>& prefix = Params().Base58Prefix(CChainParams::EXT_SECRET_KEY);
|
||||
if (data.size() == BIP32_EXTKEY_SIZE + prefix.size() && std::equal(prefix.begin(), prefix.end(), data.begin())) {
|
||||
key.Decode(data.data() + prefix.size());
|
||||
}
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
std::string EncodeExtKey(const CExtKey& key)
|
||||
{
|
||||
std::vector<unsigned char> data = Params().Base58Prefix(CChainParams::EXT_SECRET_KEY);
|
||||
size_t size = data.size();
|
||||
data.resize(size + BIP32_EXTKEY_SIZE);
|
||||
key.Encode(data.data() + size);
|
||||
std::string ret = EncodeBase58Check(data);
|
||||
memory_cleanse(data.data(), data.size());
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string EncodeDestination(const CTxDestination& dest)
|
||||
{
|
||||
return boost::apply_visitor(DestinationEncoder(Params()), dest);
|
||||
}
|
||||
|
||||
CTxDestination DecodeDestination(const std::string& str)
|
||||
{
|
||||
return DecodeDestination(str, Params());
|
||||
}
|
||||
|
||||
bool IsValidDestinationString(const std::string& str, const CChainParams& params)
|
||||
{
|
||||
return IsValidDestination(DecodeDestination(str, params));
|
||||
}
|
||||
|
||||
bool IsValidDestinationString(const std::string& str)
|
||||
{
|
||||
return IsValidDestinationString(str, Params());
|
||||
}
|
||||
|
||||
std::string EncodePaymentAddress(const libzcash::PaymentAddress& zaddr)
|
||||
{
|
||||
return boost::apply_visitor(PaymentAddressEncoder(Params()), zaddr);
|
||||
}
|
||||
|
||||
libzcash::PaymentAddress DecodePaymentAddress(const std::string& str)
|
||||
{
|
||||
std::vector<unsigned char> data;
|
||||
if (DecodeBase58Check(str, data)) {
|
||||
const std::vector<unsigned char>& zaddr_prefix = Params().Base58Prefix(CChainParams::ZCPAYMENT_ADDRRESS);
|
||||
if ((data.size() == libzcash::SerializedSproutPaymentAddressSize + zaddr_prefix.size()) &&
|
||||
std::equal(zaddr_prefix.begin(), zaddr_prefix.end(), data.begin())) {
|
||||
CSerializeData serialized(data.begin() + zaddr_prefix.size(), data.end());
|
||||
CDataStream ss(serialized, SER_NETWORK, PROTOCOL_VERSION);
|
||||
libzcash::SproutPaymentAddress ret;
|
||||
ss >> ret;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
data.clear();
|
||||
auto bech = bech32::Decode(str);
|
||||
bool allowSapling = Params().NetworkIDString() == "regtest" || (
|
||||
Params().NetworkIDString() == "test" &&
|
||||
GetBoolArg("-experimentalfeatures", false) &&
|
||||
GetBoolArg("-developersapling", false));
|
||||
if (allowSapling && bech.first == Params().Bech32HRP(CChainParams::SAPLING_PAYMENT_ADDRESS) &&
|
||||
bech.second.size() == ConvertedSaplingPaymentAddressSize) {
|
||||
// Bech32 decoding
|
||||
data.reserve((bech.second.size() * 5) / 8);
|
||||
if (ConvertBits<5, 8, false>([&](unsigned char c) { data.push_back(c); }, bech.second.begin(), bech.second.end())) {
|
||||
CDataStream ss(data, SER_NETWORK, PROTOCOL_VERSION);
|
||||
libzcash::SaplingPaymentAddress ret;
|
||||
ss >> ret;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return libzcash::InvalidEncoding();
|
||||
}
|
||||
|
||||
bool IsValidPaymentAddressString(const std::string& str) {
|
||||
return IsValidPaymentAddress(DecodePaymentAddress(str));
|
||||
}
|
||||
|
||||
std::string EncodeViewingKey(const libzcash::ViewingKey& vk)
|
||||
{
|
||||
return boost::apply_visitor(ViewingKeyEncoder(Params()), vk);
|
||||
}
|
||||
|
||||
libzcash::ViewingKey DecodeViewingKey(const std::string& str)
|
||||
{
|
||||
std::vector<unsigned char> data;
|
||||
if (DecodeBase58Check(str, data)) {
|
||||
const std::vector<unsigned char>& vk_prefix = Params().Base58Prefix(CChainParams::ZCVIEWING_KEY);
|
||||
if ((data.size() == libzcash::SerializedSproutViewingKeySize + vk_prefix.size()) &&
|
||||
std::equal(vk_prefix.begin(), vk_prefix.end(), data.begin())) {
|
||||
CSerializeData serialized(data.begin() + vk_prefix.size(), data.end());
|
||||
CDataStream ss(serialized, SER_NETWORK, PROTOCOL_VERSION);
|
||||
libzcash::SproutViewingKey ret;
|
||||
ss >> ret;
|
||||
memory_cleanse(serialized.data(), serialized.size());
|
||||
memory_cleanse(data.data(), data.size());
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
memory_cleanse(data.data(), data.size());
|
||||
return libzcash::InvalidEncoding();
|
||||
}
|
||||
|
||||
std::string EncodeSpendingKey(const libzcash::SpendingKey& zkey)
|
||||
{
|
||||
return boost::apply_visitor(SpendingKeyEncoder(Params()), zkey);
|
||||
}
|
||||
|
||||
libzcash::SpendingKey DecodeSpendingKey(const std::string& str)
|
||||
{
|
||||
std::vector<unsigned char> data;
|
||||
if (DecodeBase58Check(str, data)) {
|
||||
const std::vector<unsigned char>& zkey_prefix = Params().Base58Prefix(CChainParams::ZCSPENDING_KEY);
|
||||
if ((data.size() == libzcash::SerializedSproutSpendingKeySize + zkey_prefix.size()) &&
|
||||
std::equal(zkey_prefix.begin(), zkey_prefix.end(), data.begin())) {
|
||||
CSerializeData serialized(data.begin() + zkey_prefix.size(), data.end());
|
||||
CDataStream ss(serialized, SER_NETWORK, PROTOCOL_VERSION);
|
||||
libzcash::SproutSpendingKey ret;
|
||||
ss >> ret;
|
||||
memory_cleanse(serialized.data(), serialized.size());
|
||||
memory_cleanse(data.data(), data.size());
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
data.clear();
|
||||
auto bech = bech32::Decode(str);
|
||||
bool allowSapling = Params().NetworkIDString() == "regtest" || (
|
||||
Params().NetworkIDString() == "test" &&
|
||||
GetBoolArg("-experimentalfeatures", false) &&
|
||||
GetBoolArg("-developersapling", false));
|
||||
if (allowSapling && bech.first == Params().Bech32HRP(CChainParams::SAPLING_EXTENDED_SPEND_KEY) &&
|
||||
bech.second.size() == ConvertedSaplingExtendedSpendingKeySize) {
|
||||
// Bech32 decoding
|
||||
data.reserve((bech.second.size() * 5) / 8);
|
||||
if (ConvertBits<5, 8, false>([&](unsigned char c) { data.push_back(c); }, bech.second.begin(), bech.second.end())) {
|
||||
CDataStream ss(data, SER_NETWORK, PROTOCOL_VERSION);
|
||||
libzcash::SaplingExtendedSpendingKey ret;
|
||||
ss >> ret;
|
||||
memory_cleanse(data.data(), data.size());
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
memory_cleanse(data.data(), data.size());
|
||||
return libzcash::InvalidEncoding();
|
||||
}
|
||||
42
src/key_io.h
Normal file
42
src/key_io.h
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-2015 The Bitcoin Core developers
|
||||
// Copyright (c) 2016-2018 The Zcash developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef BITCOIN_KEYIO_H
|
||||
#define BITCOIN_KEYIO_H
|
||||
|
||||
#include <chainparams.h>
|
||||
#include <key.h>
|
||||
#include <pubkey.h>
|
||||
#include <script/standard.h>
|
||||
#include <zcash/Address.hpp>
|
||||
#include <zcash/zip32.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
CKey DecodeSecret(const std::string& str);
|
||||
std::string EncodeSecret(const CKey& key);
|
||||
|
||||
CExtKey DecodeExtKey(const std::string& str);
|
||||
std::string EncodeExtKey(const CExtKey& extkey);
|
||||
CExtPubKey DecodeExtPubKey(const std::string& str);
|
||||
std::string EncodeExtPubKey(const CExtPubKey& extpubkey);
|
||||
|
||||
std::string EncodeDestination(const CTxDestination& dest);
|
||||
CTxDestination DecodeDestination(const std::string& str);
|
||||
bool IsValidDestinationString(const std::string& str);
|
||||
bool IsValidDestinationString(const std::string& str, const CChainParams& params);
|
||||
|
||||
std::string EncodePaymentAddress(const libzcash::PaymentAddress& zaddr);
|
||||
libzcash::PaymentAddress DecodePaymentAddress(const std::string& str);
|
||||
bool IsValidPaymentAddressString(const std::string& str);
|
||||
|
||||
std::string EncodeViewingKey(const libzcash::ViewingKey& vk);
|
||||
libzcash::ViewingKey DecodeViewingKey(const std::string& str);
|
||||
|
||||
std::string EncodeSpendingKey(const libzcash::SpendingKey& zkey);
|
||||
libzcash::SpendingKey DecodeSpendingKey(const std::string& str);
|
||||
|
||||
#endif // BITCOIN_KEYIO_H
|
||||
124
src/keystore.cpp
124
src/keystore.cpp
@@ -23,6 +23,35 @@ bool CKeyStore::AddKey(const CKey &key) {
|
||||
return AddKeyPubKey(key, key.GetPubKey());
|
||||
}
|
||||
|
||||
bool CBasicKeyStore::SetHDSeed(const HDSeed& seed)
|
||||
{
|
||||
LOCK(cs_SpendingKeyStore);
|
||||
if (!hdSeed.IsNull()) {
|
||||
// Don't allow an existing seed to be changed. We can maybe relax this
|
||||
// restriction later once we have worked out the UX implications.
|
||||
return false;
|
||||
}
|
||||
hdSeed = seed;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBasicKeyStore::HaveHDSeed() const
|
||||
{
|
||||
LOCK(cs_SpendingKeyStore);
|
||||
return !hdSeed.IsNull();
|
||||
}
|
||||
|
||||
bool CBasicKeyStore::GetHDSeed(HDSeed& seedOut) const
|
||||
{
|
||||
LOCK(cs_SpendingKeyStore);
|
||||
if (hdSeed.IsNull()) {
|
||||
return false;
|
||||
} else {
|
||||
seedOut = hdSeed;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool CBasicKeyStore::AddKeyPubKey(const CKey& key, const CPubKey &pubkey)
|
||||
{
|
||||
LOCK(cs_KeyStore);
|
||||
@@ -84,45 +113,116 @@ bool CBasicKeyStore::HaveWatchOnly() const
|
||||
return (!setWatchOnly.empty());
|
||||
}
|
||||
|
||||
bool CBasicKeyStore::AddSpendingKey(const libzcash::SpendingKey &sk)
|
||||
bool CBasicKeyStore::AddSproutSpendingKey(const libzcash::SproutSpendingKey &sk)
|
||||
{
|
||||
LOCK(cs_SpendingKeyStore);
|
||||
auto address = sk.address();
|
||||
mapSpendingKeys[address] = sk;
|
||||
mapSproutSpendingKeys[address] = sk;
|
||||
mapNoteDecryptors.insert(std::make_pair(address, ZCNoteDecryption(sk.receiving_key())));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBasicKeyStore::AddViewingKey(const libzcash::ViewingKey &vk)
|
||||
//! Sapling
|
||||
bool CBasicKeyStore::AddSaplingSpendingKey(
|
||||
const libzcash::SaplingExtendedSpendingKey &sk,
|
||||
const boost::optional<libzcash::SaplingPaymentAddress> &defaultAddr)
|
||||
{
|
||||
LOCK(cs_SpendingKeyStore);
|
||||
auto fvk = sk.expsk.full_viewing_key();
|
||||
|
||||
// if SaplingFullViewingKey is not in SaplingFullViewingKeyMap, add it
|
||||
if (!AddSaplingFullViewingKey(fvk, defaultAddr)){
|
||||
return false;
|
||||
}
|
||||
|
||||
mapSaplingSpendingKeys[fvk] = sk;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBasicKeyStore::AddSproutViewingKey(const libzcash::SproutViewingKey &vk)
|
||||
{
|
||||
LOCK(cs_SpendingKeyStore);
|
||||
auto address = vk.address();
|
||||
mapViewingKeys[address] = vk;
|
||||
mapSproutViewingKeys[address] = vk;
|
||||
mapNoteDecryptors.insert(std::make_pair(address, ZCNoteDecryption(vk.sk_enc)));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBasicKeyStore::RemoveViewingKey(const libzcash::ViewingKey &vk)
|
||||
bool CBasicKeyStore::AddSaplingFullViewingKey(
|
||||
const libzcash::SaplingFullViewingKey &fvk,
|
||||
const boost::optional<libzcash::SaplingPaymentAddress> &defaultAddr)
|
||||
{
|
||||
LOCK(cs_SpendingKeyStore);
|
||||
mapViewingKeys.erase(vk.address());
|
||||
auto ivk = fvk.in_viewing_key();
|
||||
mapSaplingFullViewingKeys[ivk] = fvk;
|
||||
|
||||
if (defaultAddr) {
|
||||
// Add defaultAddr -> SaplingIncomingViewing to SaplingIncomingViewingKeyMap
|
||||
mapSaplingIncomingViewingKeys[defaultAddr.get()] = ivk;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBasicKeyStore::HaveViewingKey(const libzcash::PaymentAddress &address) const
|
||||
bool CBasicKeyStore::RemoveSproutViewingKey(const libzcash::SproutViewingKey &vk)
|
||||
{
|
||||
LOCK(cs_SpendingKeyStore);
|
||||
return mapViewingKeys.count(address) > 0;
|
||||
mapSproutViewingKeys.erase(vk.address());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBasicKeyStore::GetViewingKey(const libzcash::PaymentAddress &address,
|
||||
libzcash::ViewingKey &vkOut) const
|
||||
bool CBasicKeyStore::HaveSproutViewingKey(const libzcash::SproutPaymentAddress &address) const
|
||||
{
|
||||
LOCK(cs_SpendingKeyStore);
|
||||
ViewingKeyMap::const_iterator mi = mapViewingKeys.find(address);
|
||||
if (mi != mapViewingKeys.end()) {
|
||||
return mapSproutViewingKeys.count(address) > 0;
|
||||
}
|
||||
|
||||
bool CBasicKeyStore::HaveSaplingFullViewingKey(const libzcash::SaplingIncomingViewingKey &ivk) const
|
||||
{
|
||||
LOCK(cs_SpendingKeyStore);
|
||||
return mapSaplingFullViewingKeys.count(ivk) > 0;
|
||||
}
|
||||
|
||||
bool CBasicKeyStore::HaveSaplingIncomingViewingKey(const libzcash::SaplingPaymentAddress &addr) const
|
||||
{
|
||||
LOCK(cs_SpendingKeyStore);
|
||||
return mapSaplingIncomingViewingKeys.count(addr) > 0;
|
||||
}
|
||||
|
||||
bool CBasicKeyStore::GetSproutViewingKey(
|
||||
const libzcash::SproutPaymentAddress &address,
|
||||
libzcash::SproutViewingKey &vkOut) const
|
||||
{
|
||||
LOCK(cs_SpendingKeyStore);
|
||||
SproutViewingKeyMap::const_iterator mi = mapSproutViewingKeys.find(address);
|
||||
if (mi != mapSproutViewingKeys.end()) {
|
||||
vkOut = mi->second;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CBasicKeyStore::GetSaplingFullViewingKey(const libzcash::SaplingIncomingViewingKey &ivk,
|
||||
libzcash::SaplingFullViewingKey &fvkOut) const
|
||||
{
|
||||
LOCK(cs_SpendingKeyStore);
|
||||
SaplingFullViewingKeyMap::const_iterator mi = mapSaplingFullViewingKeys.find(ivk);
|
||||
if (mi != mapSaplingFullViewingKeys.end()) {
|
||||
fvkOut = mi->second;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CBasicKeyStore::GetSaplingIncomingViewingKey(const libzcash::SaplingPaymentAddress &addr,
|
||||
libzcash::SaplingIncomingViewingKey &ivkOut) const
|
||||
{
|
||||
LOCK(cs_SpendingKeyStore);
|
||||
SaplingIncomingViewingKeyMap::const_iterator mi = mapSaplingIncomingViewingKeys.find(addr);
|
||||
if (mi != mapSaplingIncomingViewingKeys.end()) {
|
||||
ivkOut = mi->second;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
171
src/keystore.h
171
src/keystore.h
@@ -14,6 +14,7 @@
|
||||
#include "sync.h"
|
||||
#include "zcash/Address.hpp"
|
||||
#include "zcash/NoteEncryption.hpp"
|
||||
#include "zcash/zip32.h"
|
||||
|
||||
#include <boost/signals2/signal.hpp>
|
||||
#include <boost/variant.hpp>
|
||||
@@ -28,6 +29,12 @@ protected:
|
||||
public:
|
||||
virtual ~CKeyStore() {}
|
||||
|
||||
//! Set the HD seed for this keystore
|
||||
virtual bool SetHDSeed(const HDSeed& seed) =0;
|
||||
virtual bool HaveHDSeed() const =0;
|
||||
//! Get the HD seed for this keystore
|
||||
virtual bool GetHDSeed(HDSeed& seedOut) const =0;
|
||||
|
||||
//! Add a key to the store.
|
||||
virtual bool AddKeyPubKey(const CKey &key, const CPubKey &pubkey) =0;
|
||||
virtual bool AddKey(const CKey &key);
|
||||
@@ -50,39 +57,82 @@ public:
|
||||
virtual bool HaveWatchOnly() const =0;
|
||||
|
||||
//! Add a spending key to the store.
|
||||
virtual bool AddSpendingKey(const libzcash::SpendingKey &sk) =0;
|
||||
virtual bool AddSproutSpendingKey(const libzcash::SproutSpendingKey &sk) =0;
|
||||
|
||||
//! Check whether a spending key corresponding to a given payment address is present in the store.
|
||||
virtual bool HaveSpendingKey(const libzcash::PaymentAddress &address) const =0;
|
||||
virtual bool GetSpendingKey(const libzcash::PaymentAddress &address, libzcash::SpendingKey& skOut) const =0;
|
||||
virtual void GetPaymentAddresses(std::set<libzcash::PaymentAddress> &setAddress) const =0;
|
||||
virtual bool HaveSproutSpendingKey(const libzcash::SproutPaymentAddress &address) const =0;
|
||||
virtual bool GetSproutSpendingKey(const libzcash::SproutPaymentAddress &address, libzcash::SproutSpendingKey& skOut) const =0;
|
||||
virtual void GetSproutPaymentAddresses(std::set<libzcash::SproutPaymentAddress> &setAddress) const =0;
|
||||
|
||||
//! Add a Sapling spending key to the store.
|
||||
virtual bool AddSaplingSpendingKey(
|
||||
const libzcash::SaplingExtendedSpendingKey &sk,
|
||||
const boost::optional<libzcash::SaplingPaymentAddress> &defaultAddr = boost::none) =0;
|
||||
|
||||
//! Check whether a Sapling spending key corresponding to a given Sapling viewing key is present in the store.
|
||||
virtual bool HaveSaplingSpendingKey(const libzcash::SaplingFullViewingKey &fvk) const =0;
|
||||
virtual bool GetSaplingSpendingKey(const libzcash::SaplingFullViewingKey &fvk, libzcash::SaplingExtendedSpendingKey& skOut) const =0;
|
||||
|
||||
//! Support for viewing keys
|
||||
virtual bool AddViewingKey(const libzcash::ViewingKey &vk) =0;
|
||||
virtual bool RemoveViewingKey(const libzcash::ViewingKey &vk) =0;
|
||||
virtual bool HaveViewingKey(const libzcash::PaymentAddress &address) const =0;
|
||||
virtual bool GetViewingKey(const libzcash::PaymentAddress &address, libzcash::ViewingKey& vkOut) const =0;
|
||||
//! Support for Sapling full viewing keys
|
||||
virtual bool AddSaplingFullViewingKey(
|
||||
const libzcash::SaplingFullViewingKey &fvk,
|
||||
const boost::optional<libzcash::SaplingPaymentAddress> &defaultAddr = boost::none) =0;
|
||||
virtual bool HaveSaplingFullViewingKey(const libzcash::SaplingIncomingViewingKey &ivk) const =0;
|
||||
virtual bool GetSaplingFullViewingKey(
|
||||
const libzcash::SaplingIncomingViewingKey &ivk,
|
||||
libzcash::SaplingFullViewingKey& fvkOut) const =0;
|
||||
|
||||
//! Sapling incoming viewing keys
|
||||
virtual bool HaveSaplingIncomingViewingKey(const libzcash::SaplingPaymentAddress &addr) const =0;
|
||||
virtual bool GetSaplingIncomingViewingKey(
|
||||
const libzcash::SaplingPaymentAddress &addr,
|
||||
libzcash::SaplingIncomingViewingKey& ivkOut) const =0;
|
||||
virtual void GetSaplingPaymentAddresses(std::set<libzcash::SaplingPaymentAddress> &setAddress) const =0;
|
||||
|
||||
//! Support for Sprout viewing keys
|
||||
virtual bool AddSproutViewingKey(const libzcash::SproutViewingKey &vk) =0;
|
||||
virtual bool RemoveSproutViewingKey(const libzcash::SproutViewingKey &vk) =0;
|
||||
virtual bool HaveSproutViewingKey(const libzcash::SproutPaymentAddress &address) const =0;
|
||||
virtual bool GetSproutViewingKey(
|
||||
const libzcash::SproutPaymentAddress &address,
|
||||
libzcash::SproutViewingKey& vkOut) const =0;
|
||||
};
|
||||
|
||||
typedef std::map<CKeyID, CKey> KeyMap;
|
||||
typedef std::map<CScriptID, CScript > ScriptMap;
|
||||
typedef std::set<CScript> WatchOnlySet;
|
||||
typedef std::map<libzcash::PaymentAddress, libzcash::SpendingKey> SpendingKeyMap;
|
||||
typedef std::map<libzcash::PaymentAddress, libzcash::ViewingKey> ViewingKeyMap;
|
||||
typedef std::map<libzcash::PaymentAddress, ZCNoteDecryption> NoteDecryptorMap;
|
||||
typedef std::map<libzcash::SproutPaymentAddress, libzcash::SproutSpendingKey> SproutSpendingKeyMap;
|
||||
typedef std::map<libzcash::SproutPaymentAddress, libzcash::SproutViewingKey> SproutViewingKeyMap;
|
||||
typedef std::map<libzcash::SproutPaymentAddress, ZCNoteDecryption> NoteDecryptorMap;
|
||||
|
||||
// Full viewing key has equivalent functionality to a transparent address
|
||||
// When encrypting wallet, encrypt SaplingSpendingKeyMap, while leaving SaplingFullViewingKeyMap unencrypted
|
||||
typedef std::map<libzcash::SaplingFullViewingKey, libzcash::SaplingExtendedSpendingKey> SaplingSpendingKeyMap;
|
||||
typedef std::map<libzcash::SaplingIncomingViewingKey, libzcash::SaplingFullViewingKey> SaplingFullViewingKeyMap;
|
||||
// Only maps from default addresses to ivk, may need to be reworked when adding diversified addresses.
|
||||
typedef std::map<libzcash::SaplingPaymentAddress, libzcash::SaplingIncomingViewingKey> SaplingIncomingViewingKeyMap;
|
||||
|
||||
/** Basic key store, that keeps keys in an address->secret map */
|
||||
class CBasicKeyStore : public CKeyStore
|
||||
{
|
||||
protected:
|
||||
HDSeed hdSeed;
|
||||
KeyMap mapKeys;
|
||||
ScriptMap mapScripts;
|
||||
WatchOnlySet setWatchOnly;
|
||||
SpendingKeyMap mapSpendingKeys;
|
||||
ViewingKeyMap mapViewingKeys;
|
||||
SproutSpendingKeyMap mapSproutSpendingKeys;
|
||||
SproutViewingKeyMap mapSproutViewingKeys;
|
||||
NoteDecryptorMap mapNoteDecryptors;
|
||||
|
||||
SaplingSpendingKeyMap mapSaplingSpendingKeys;
|
||||
SaplingFullViewingKeyMap mapSaplingFullViewingKeys;
|
||||
SaplingIncomingViewingKeyMap mapSaplingIncomingViewingKeys;
|
||||
|
||||
public:
|
||||
bool SetHDSeed(const HDSeed& seed);
|
||||
bool HaveHDSeed() const;
|
||||
bool GetHDSeed(HDSeed& seedOut) const;
|
||||
|
||||
bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey);
|
||||
bool HaveKey(const CKeyID &address) const
|
||||
{
|
||||
@@ -128,22 +178,22 @@ public:
|
||||
virtual bool HaveWatchOnly(const CScript &dest) const;
|
||||
virtual bool HaveWatchOnly() const;
|
||||
|
||||
bool AddSpendingKey(const libzcash::SpendingKey &sk);
|
||||
bool HaveSpendingKey(const libzcash::PaymentAddress &address) const
|
||||
bool AddSproutSpendingKey(const libzcash::SproutSpendingKey &sk);
|
||||
bool HaveSproutSpendingKey(const libzcash::SproutPaymentAddress &address) const
|
||||
{
|
||||
bool result;
|
||||
{
|
||||
LOCK(cs_SpendingKeyStore);
|
||||
result = (mapSpendingKeys.count(address) > 0);
|
||||
result = (mapSproutSpendingKeys.count(address) > 0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
bool GetSpendingKey(const libzcash::PaymentAddress &address, libzcash::SpendingKey &skOut) const
|
||||
bool GetSproutSpendingKey(const libzcash::SproutPaymentAddress &address, libzcash::SproutSpendingKey &skOut) const
|
||||
{
|
||||
{
|
||||
LOCK(cs_SpendingKeyStore);
|
||||
SpendingKeyMap::const_iterator mi = mapSpendingKeys.find(address);
|
||||
if (mi != mapSpendingKeys.end())
|
||||
SproutSpendingKeyMap::const_iterator mi = mapSproutSpendingKeys.find(address);
|
||||
if (mi != mapSproutSpendingKeys.end())
|
||||
{
|
||||
skOut = mi->second;
|
||||
return true;
|
||||
@@ -151,7 +201,7 @@ public:
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool GetNoteDecryptor(const libzcash::PaymentAddress &address, ZCNoteDecryption &decOut) const
|
||||
bool GetNoteDecryptor(const libzcash::SproutPaymentAddress &address, ZCNoteDecryption &decOut) const
|
||||
{
|
||||
{
|
||||
LOCK(cs_SpendingKeyStore);
|
||||
@@ -164,34 +214,93 @@ public:
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void GetPaymentAddresses(std::set<libzcash::PaymentAddress> &setAddress) const
|
||||
void GetSproutPaymentAddresses(std::set<libzcash::SproutPaymentAddress> &setAddress) const
|
||||
{
|
||||
setAddress.clear();
|
||||
{
|
||||
LOCK(cs_SpendingKeyStore);
|
||||
SpendingKeyMap::const_iterator mi = mapSpendingKeys.begin();
|
||||
while (mi != mapSpendingKeys.end())
|
||||
SproutSpendingKeyMap::const_iterator mi = mapSproutSpendingKeys.begin();
|
||||
while (mi != mapSproutSpendingKeys.end())
|
||||
{
|
||||
setAddress.insert((*mi).first);
|
||||
mi++;
|
||||
}
|
||||
ViewingKeyMap::const_iterator mvi = mapViewingKeys.begin();
|
||||
while (mvi != mapViewingKeys.end())
|
||||
SproutViewingKeyMap::const_iterator mvi = mapSproutViewingKeys.begin();
|
||||
while (mvi != mapSproutViewingKeys.end())
|
||||
{
|
||||
setAddress.insert((*mvi).first);
|
||||
mvi++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//! Sapling
|
||||
bool AddSaplingSpendingKey(
|
||||
const libzcash::SaplingExtendedSpendingKey &sk,
|
||||
const boost::optional<libzcash::SaplingPaymentAddress> &defaultAddr = boost::none);
|
||||
bool HaveSaplingSpendingKey(const libzcash::SaplingFullViewingKey &fvk) const
|
||||
{
|
||||
bool result;
|
||||
{
|
||||
LOCK(cs_SpendingKeyStore);
|
||||
result = (mapSaplingSpendingKeys.count(fvk) > 0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
bool GetSaplingSpendingKey(const libzcash::SaplingFullViewingKey &fvk, libzcash::SaplingExtendedSpendingKey &skOut) const
|
||||
{
|
||||
{
|
||||
LOCK(cs_SpendingKeyStore);
|
||||
|
||||
SaplingSpendingKeyMap::const_iterator mi = mapSaplingSpendingKeys.find(fvk);
|
||||
if (mi != mapSaplingSpendingKeys.end())
|
||||
{
|
||||
skOut = mi->second;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool AddViewingKey(const libzcash::ViewingKey &vk);
|
||||
virtual bool RemoveViewingKey(const libzcash::ViewingKey &vk);
|
||||
virtual bool HaveViewingKey(const libzcash::PaymentAddress &address) const;
|
||||
virtual bool GetViewingKey(const libzcash::PaymentAddress &address, libzcash::ViewingKey& vkOut) const;
|
||||
virtual bool AddSaplingFullViewingKey(
|
||||
const libzcash::SaplingFullViewingKey &fvk,
|
||||
const boost::optional<libzcash::SaplingPaymentAddress> &defaultAddr = boost::none);
|
||||
virtual bool HaveSaplingFullViewingKey(const libzcash::SaplingIncomingViewingKey &ivk) const;
|
||||
virtual bool GetSaplingFullViewingKey(
|
||||
const libzcash::SaplingIncomingViewingKey &ivk,
|
||||
libzcash::SaplingFullViewingKey& fvkOut) const;
|
||||
|
||||
virtual bool HaveSaplingIncomingViewingKey(const libzcash::SaplingPaymentAddress &addr) const;
|
||||
virtual bool GetSaplingIncomingViewingKey(
|
||||
const libzcash::SaplingPaymentAddress &addr,
|
||||
libzcash::SaplingIncomingViewingKey& ivkOut) const;
|
||||
void GetSaplingPaymentAddresses(std::set<libzcash::SaplingPaymentAddress> &setAddress) const
|
||||
{
|
||||
setAddress.clear();
|
||||
{
|
||||
LOCK(cs_SpendingKeyStore);
|
||||
auto mi = mapSaplingIncomingViewingKeys.begin();
|
||||
while (mi != mapSaplingIncomingViewingKeys.end())
|
||||
{
|
||||
setAddress.insert((*mi).first);
|
||||
mi++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool AddSproutViewingKey(const libzcash::SproutViewingKey &vk);
|
||||
virtual bool RemoveSproutViewingKey(const libzcash::SproutViewingKey &vk);
|
||||
virtual bool HaveSproutViewingKey(const libzcash::SproutPaymentAddress &address) const;
|
||||
virtual bool GetSproutViewingKey(
|
||||
const libzcash::SproutPaymentAddress &address,
|
||||
libzcash::SproutViewingKey& vkOut) const;
|
||||
};
|
||||
|
||||
typedef std::vector<unsigned char, secure_allocator<unsigned char> > CKeyingMaterial;
|
||||
typedef std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char> > > CryptedKeyMap;
|
||||
typedef std::map<libzcash::PaymentAddress, std::vector<unsigned char> > CryptedSpendingKeyMap;
|
||||
typedef std::map<libzcash::SproutPaymentAddress, std::vector<unsigned char> > CryptedSproutSpendingKeyMap;
|
||||
|
||||
//! Sapling
|
||||
typedef std::map<libzcash::SaplingFullViewingKey, std::vector<unsigned char> > CryptedSaplingSpendingKeyMap;
|
||||
|
||||
#endif // BITCOIN_KEYSTORE_H
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "base58.h"
|
||||
#include "clientversion.h"
|
||||
#include "coins.h"
|
||||
#include "consensus/consensus.h"
|
||||
#include "consensus/upgrades.h"
|
||||
#include "core_io.h"
|
||||
#include "key_io.h"
|
||||
#include "keystore.h"
|
||||
#include "primitives/transaction.h"
|
||||
#include "script/script.h"
|
||||
@@ -38,9 +38,14 @@ uint64_t komodo_accrued_interest(int32_t *txheightp,uint32_t *locktimep,uint256
|
||||
}
|
||||
|
||||
static bool fCreateBlank;
|
||||
static map<string,UniValue> registers;
|
||||
static std::map<std::string,UniValue> registers;
|
||||
static const int CONTINUE_EXECUTION=-1;
|
||||
|
||||
static bool AppInitRawTx(int argc, char* argv[])
|
||||
//
|
||||
// This function returns either one of EXIT_ codes when it's expected to stop the process or
|
||||
// CONTINUE_EXECUTION when it's expected to continue further.
|
||||
//
|
||||
static int AppInitRawTx(int argc, char* argv[])
|
||||
{
|
||||
//
|
||||
// Parameters
|
||||
@@ -79,7 +84,7 @@ static bool AppInitRawTx(int argc, char* argv[])
|
||||
strUsage = HelpMessageGroup(_("Commands:"));
|
||||
strUsage += HelpMessageOpt("delin=N", _("Delete input N from TX"));
|
||||
strUsage += HelpMessageOpt("delout=N", _("Delete output N from TX"));
|
||||
strUsage += HelpMessageOpt("in=TXID:VOUT", _("Add input to TX"));
|
||||
strUsage += HelpMessageOpt("in=TXID:VOUT(:SEQUENCE_NUMBER)", _("Add input to TX"));
|
||||
strUsage += HelpMessageOpt("locktime=N", _("Set TX lock time to N"));
|
||||
strUsage += HelpMessageOpt("nversion=N", _("Set TX version to N"));
|
||||
strUsage += HelpMessageOpt("outaddr=VALUE:ADDRESS", _("Add address-based output to TX"));
|
||||
@@ -96,57 +101,61 @@ static bool AppInitRawTx(int argc, char* argv[])
|
||||
strUsage += HelpMessageOpt("set=NAME:JSON-STRING", _("Set register NAME to given JSON-STRING"));
|
||||
fprintf(stdout, "%s", strUsage.c_str());
|
||||
|
||||
return false;
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "Error: too few parameters\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
return true;
|
||||
return CONTINUE_EXECUTION;
|
||||
}
|
||||
|
||||
static void RegisterSetJson(const string& key, const string& rawJson)
|
||||
static void RegisterSetJson(const std::string& key, const std::string& rawJson)
|
||||
{
|
||||
UniValue val;
|
||||
if (!val.read(rawJson)) {
|
||||
string strErr = "Cannot parse JSON for key " + key;
|
||||
throw runtime_error(strErr);
|
||||
std::string strErr = "Cannot parse JSON for key " + key;
|
||||
throw std::runtime_error(strErr);
|
||||
}
|
||||
|
||||
registers[key] = val;
|
||||
}
|
||||
|
||||
static void RegisterSet(const string& strInput)
|
||||
static void RegisterSet(const std::string& strInput)
|
||||
{
|
||||
// separate NAME:VALUE in string
|
||||
size_t pos = strInput.find(':');
|
||||
if ((pos == string::npos) ||
|
||||
if ((pos == std::string::npos) ||
|
||||
(pos == 0) ||
|
||||
(pos == (strInput.size() - 1)))
|
||||
throw runtime_error("Register input requires NAME:VALUE");
|
||||
throw std::runtime_error("Register input requires NAME:VALUE");
|
||||
|
||||
string key = strInput.substr(0, pos);
|
||||
string valStr = strInput.substr(pos + 1, string::npos);
|
||||
std::string key = strInput.substr(0, pos);
|
||||
std::string valStr = strInput.substr(pos + 1, std::string::npos);
|
||||
|
||||
RegisterSetJson(key, valStr);
|
||||
}
|
||||
|
||||
static void RegisterLoad(const string& strInput)
|
||||
static void RegisterLoad(const std::string& strInput)
|
||||
{
|
||||
// separate NAME:FILENAME in string
|
||||
size_t pos = strInput.find(':');
|
||||
if ((pos == string::npos) ||
|
||||
if ((pos == std::string::npos) ||
|
||||
(pos == 0) ||
|
||||
(pos == (strInput.size() - 1)))
|
||||
throw runtime_error("Register load requires NAME:FILENAME");
|
||||
throw std::runtime_error("Register load requires NAME:FILENAME");
|
||||
|
||||
string key = strInput.substr(0, pos);
|
||||
string filename = strInput.substr(pos + 1, string::npos);
|
||||
std::string key = strInput.substr(0, pos);
|
||||
std::string filename = strInput.substr(pos + 1, std::string::npos);
|
||||
|
||||
FILE *f = fopen(filename.c_str(), "r");
|
||||
if (!f) {
|
||||
string strErr = "Cannot open file " + filename;
|
||||
throw runtime_error(strErr);
|
||||
std::string strErr = "Cannot open file " + filename;
|
||||
throw std::runtime_error(strErr);
|
||||
}
|
||||
|
||||
// load file chunks into one big buffer
|
||||
string valStr;
|
||||
std::string valStr;
|
||||
while ((!feof(f)) && (!ferror(f))) {
|
||||
char buf[4096];
|
||||
int bread = fread(buf, 1, sizeof(buf), f);
|
||||
@@ -160,115 +169,119 @@ static void RegisterLoad(const string& strInput)
|
||||
fclose(f);
|
||||
|
||||
if (error) {
|
||||
string strErr = "Error reading file " + filename;
|
||||
throw runtime_error(strErr);
|
||||
std::string strErr = "Error reading file " + filename;
|
||||
throw std::runtime_error(strErr);
|
||||
}
|
||||
|
||||
// evaluate as JSON buffer register
|
||||
RegisterSetJson(key, valStr);
|
||||
}
|
||||
|
||||
static void MutateTxVersion(CMutableTransaction& tx, const string& cmdVal)
|
||||
static void MutateTxVersion(CMutableTransaction& tx, const std::string& cmdVal)
|
||||
{
|
||||
int64_t newVersion = atoi64(cmdVal);
|
||||
if (newVersion < CTransaction::SPROUT_MIN_CURRENT_VERSION || newVersion > CTransaction::SPROUT_MAX_CURRENT_VERSION)
|
||||
throw runtime_error("Invalid TX version requested");
|
||||
throw std::runtime_error("Invalid TX version requested");
|
||||
|
||||
tx.nVersion = (int) newVersion;
|
||||
}
|
||||
|
||||
static void MutateTxExpiry(CMutableTransaction& tx, const string& cmdVal)
|
||||
static void MutateTxExpiry(CMutableTransaction& tx, const std::string& cmdVal)
|
||||
{
|
||||
int64_t newExpiry = atoi64(cmdVal);
|
||||
if (newExpiry >= TX_EXPIRY_HEIGHT_THRESHOLD) {
|
||||
throw runtime_error("Invalid TX expiry requested");
|
||||
throw std::runtime_error("Invalid TX expiry requested");
|
||||
}
|
||||
tx.nExpiryHeight = (int) newExpiry;
|
||||
}
|
||||
|
||||
static void MutateTxLocktime(CMutableTransaction& tx, const string& cmdVal)
|
||||
static void MutateTxLocktime(CMutableTransaction& tx, const std::string& cmdVal)
|
||||
{
|
||||
int64_t newLocktime = atoi64(cmdVal);
|
||||
if (newLocktime < 0LL || newLocktime > 0xffffffffLL)
|
||||
throw runtime_error("Invalid TX locktime requested");
|
||||
throw std::runtime_error("Invalid TX locktime requested");
|
||||
|
||||
tx.nLockTime = (unsigned int) newLocktime;
|
||||
}
|
||||
|
||||
static void MutateTxAddInput(CMutableTransaction& tx, const string& strInput)
|
||||
static void MutateTxAddInput(CMutableTransaction& tx, const std::string& strInput)
|
||||
{
|
||||
std::vector<std::string> vStrInputParts;
|
||||
boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
|
||||
|
||||
// separate TXID:VOUT in string
|
||||
size_t pos = strInput.find(':');
|
||||
if ((pos == string::npos) ||
|
||||
(pos == 0) ||
|
||||
(pos == (strInput.size() - 1)))
|
||||
throw runtime_error("TX input missing separator");
|
||||
if (vStrInputParts.size()<2)
|
||||
throw std::runtime_error("TX input missing separator");
|
||||
|
||||
// extract and validate TXID
|
||||
string strTxid = strInput.substr(0, pos);
|
||||
std::string strTxid = vStrInputParts[0];
|
||||
if ((strTxid.size() != 64) || !IsHex(strTxid))
|
||||
throw runtime_error("invalid TX input txid");
|
||||
throw std::runtime_error("invalid TX input txid");
|
||||
uint256 txid(uint256S(strTxid));
|
||||
|
||||
static const unsigned int minTxOutSz = 9;
|
||||
static const unsigned int maxVout = MAX_BLOCK_SIZE / minTxOutSz;
|
||||
|
||||
// extract and validate vout
|
||||
string strVout = strInput.substr(pos + 1, string::npos);
|
||||
std::string strVout = vStrInputParts[1];
|
||||
int vout = atoi(strVout);
|
||||
if ((vout < 0) || (vout > (int)maxVout))
|
||||
throw runtime_error("invalid TX input vout");
|
||||
throw std::runtime_error("invalid TX input vout");
|
||||
|
||||
// extract the optional sequence number
|
||||
uint32_t nSequenceIn=std::numeric_limits<unsigned int>::max();
|
||||
if (vStrInputParts.size() > 2)
|
||||
nSequenceIn = atoi(vStrInputParts[2]);
|
||||
|
||||
// append to transaction input list
|
||||
CTxIn txin(txid, vout);
|
||||
CTxIn txin(txid, vout, CScript(), nSequenceIn);
|
||||
tx.vin.push_back(txin);
|
||||
}
|
||||
|
||||
static void MutateTxAddOutAddr(CMutableTransaction& tx, const string& strInput)
|
||||
static void MutateTxAddOutAddr(CMutableTransaction& tx, const std::string& strInput)
|
||||
{
|
||||
// separate VALUE:ADDRESS in string
|
||||
size_t pos = strInput.find(':');
|
||||
if ((pos == string::npos) ||
|
||||
if ((pos == std::string::npos) ||
|
||||
(pos == 0) ||
|
||||
(pos == (strInput.size() - 1)))
|
||||
throw runtime_error("TX output missing separator");
|
||||
throw std::runtime_error("TX output missing separator");
|
||||
|
||||
// extract and validate VALUE
|
||||
string strValue = strInput.substr(0, pos);
|
||||
std::string strValue = strInput.substr(0, pos);
|
||||
CAmount value;
|
||||
if (!ParseMoney(strValue, value))
|
||||
throw runtime_error("invalid TX output value");
|
||||
throw std::runtime_error("invalid TX output value");
|
||||
|
||||
// extract and validate ADDRESS
|
||||
string strAddr = strInput.substr(pos + 1, string::npos);
|
||||
CBitcoinAddress addr(strAddr);
|
||||
if (!addr.IsValid())
|
||||
throw runtime_error("invalid TX output address");
|
||||
|
||||
// build standard output script via GetScriptForDestination()
|
||||
CScript scriptPubKey = GetScriptForDestination(addr.Get());
|
||||
std::string strAddr = strInput.substr(pos + 1, std::string::npos);
|
||||
CTxDestination destination = DecodeDestination(strAddr);
|
||||
if (!IsValidDestination(destination)) {
|
||||
throw std::runtime_error("invalid TX output address");
|
||||
}
|
||||
CScript scriptPubKey = GetScriptForDestination(destination);
|
||||
|
||||
// construct TxOut, append to transaction output list
|
||||
CTxOut txout(value, scriptPubKey);
|
||||
tx.vout.push_back(txout);
|
||||
}
|
||||
|
||||
static void MutateTxAddOutScript(CMutableTransaction& tx, const string& strInput)
|
||||
static void MutateTxAddOutScript(CMutableTransaction& tx, const std::string& strInput)
|
||||
{
|
||||
// separate VALUE:SCRIPT in string
|
||||
size_t pos = strInput.find(':');
|
||||
if ((pos == string::npos) ||
|
||||
if ((pos == std::string::npos) ||
|
||||
(pos == 0))
|
||||
throw runtime_error("TX output missing separator");
|
||||
throw std::runtime_error("TX output missing separator");
|
||||
|
||||
// extract and validate VALUE
|
||||
string strValue = strInput.substr(0, pos);
|
||||
std::string strValue = strInput.substr(0, pos);
|
||||
CAmount value;
|
||||
if (!ParseMoney(strValue, value))
|
||||
throw runtime_error("invalid TX output value");
|
||||
throw std::runtime_error("invalid TX output value");
|
||||
|
||||
// extract and validate script
|
||||
string strScript = strInput.substr(pos + 1, string::npos);
|
||||
std::string strScript = strInput.substr(pos + 1, std::string::npos);
|
||||
CScript scriptPubKey = ParseScript(strScript); // throws on err
|
||||
|
||||
// construct TxOut, append to transaction output list
|
||||
@@ -276,26 +289,26 @@ static void MutateTxAddOutScript(CMutableTransaction& tx, const string& strInput
|
||||
tx.vout.push_back(txout);
|
||||
}
|
||||
|
||||
static void MutateTxDelInput(CMutableTransaction& tx, const string& strInIdx)
|
||||
static void MutateTxDelInput(CMutableTransaction& tx, const std::string& strInIdx)
|
||||
{
|
||||
// parse requested deletion index
|
||||
int inIdx = atoi(strInIdx);
|
||||
if (inIdx < 0 || inIdx >= (int)tx.vin.size()) {
|
||||
string strErr = "Invalid TX input index '" + strInIdx + "'";
|
||||
throw runtime_error(strErr.c_str());
|
||||
std::string strErr = "Invalid TX input index '" + strInIdx + "'";
|
||||
throw std::runtime_error(strErr.c_str());
|
||||
}
|
||||
|
||||
// delete input from transaction
|
||||
tx.vin.erase(tx.vin.begin() + inIdx);
|
||||
}
|
||||
|
||||
static void MutateTxDelOutput(CMutableTransaction& tx, const string& strOutIdx)
|
||||
static void MutateTxDelOutput(CMutableTransaction& tx, const std::string& strOutIdx)
|
||||
{
|
||||
// parse requested deletion index
|
||||
int outIdx = atoi(strOutIdx);
|
||||
if (outIdx < 0 || outIdx >= (int)tx.vout.size()) {
|
||||
string strErr = "Invalid TX output index '" + strOutIdx + "'";
|
||||
throw runtime_error(strErr.c_str());
|
||||
std::string strErr = "Invalid TX output index '" + strOutIdx + "'";
|
||||
throw std::runtime_error(strErr.c_str());
|
||||
}
|
||||
|
||||
// delete output from transaction
|
||||
@@ -315,7 +328,7 @@ static const struct {
|
||||
{"SINGLE|ANYONECANPAY", SIGHASH_SINGLE|SIGHASH_ANYONECANPAY},
|
||||
};
|
||||
|
||||
static bool findSighashFlags(int& flags, const string& flagStr)
|
||||
static bool findSighashFlags(int& flags, const std::string& flagStr)
|
||||
{
|
||||
flags = 0;
|
||||
|
||||
@@ -329,17 +342,17 @@ static bool findSighashFlags(int& flags, const string& flagStr)
|
||||
return false;
|
||||
}
|
||||
|
||||
uint256 ParseHashUO(map<string,UniValue>& o, string strKey)
|
||||
uint256 ParseHashUO(std::map<std::string,UniValue>& o, std::string strKey)
|
||||
{
|
||||
if (!o.count(strKey))
|
||||
return uint256();
|
||||
return ParseHashUV(o[strKey], strKey);
|
||||
}
|
||||
|
||||
vector<unsigned char> ParseHexUO(map<string,UniValue>& o, string strKey)
|
||||
std::vector<unsigned char> ParseHexUO(std::map<std::string,UniValue>& o, std::string strKey)
|
||||
{
|
||||
if (!o.count(strKey)) {
|
||||
vector<unsigned char> emptyVec;
|
||||
std::vector<unsigned char> emptyVec;
|
||||
return emptyVec;
|
||||
}
|
||||
return ParseHexUV(o[strKey], strKey);
|
||||
@@ -348,41 +361,41 @@ vector<unsigned char> ParseHexUO(map<string,UniValue>& o, string strKey)
|
||||
static CAmount AmountFromValue(const UniValue& value)
|
||||
{
|
||||
if (!value.isNum() && !value.isStr())
|
||||
throw runtime_error("Amount is not a number or string");
|
||||
throw std::runtime_error("Amount is not a number or string");
|
||||
CAmount amount;
|
||||
if (!ParseFixedPoint(value.getValStr(), 8, &amount))
|
||||
throw runtime_error("Invalid amount");
|
||||
throw std::runtime_error("Invalid amount");
|
||||
if (!MoneyRange(amount))
|
||||
throw runtime_error("Amount out of range");
|
||||
throw std::runtime_error("Amount out of range");
|
||||
return amount;
|
||||
}
|
||||
|
||||
static void MutateTxSign(CMutableTransaction& tx, const string& strInput)
|
||||
static void MutateTxSign(CMutableTransaction& tx, const std::string& strInput)
|
||||
{
|
||||
// separate HEIGHT:SIGHASH-FLAGS in string
|
||||
size_t pos = strInput.find(':');
|
||||
if ((pos == 0) ||
|
||||
(pos == (strInput.size() - 1)))
|
||||
throw runtime_error("Invalid sighash flag separator");
|
||||
throw std::runtime_error("Invalid sighash flag separator");
|
||||
|
||||
// extract and validate HEIGHT
|
||||
string strHeight = strInput.substr(0, pos);
|
||||
std::string strHeight = strInput.substr(0, pos);
|
||||
int nHeight = atoi(strHeight);
|
||||
if (nHeight <= 0) {
|
||||
throw runtime_error("invalid height");
|
||||
throw std::runtime_error("invalid height");
|
||||
}
|
||||
|
||||
// extract and validate SIGHASH-FLAGS
|
||||
int nHashType = SIGHASH_ALL;
|
||||
string flagStr;
|
||||
if (pos != string::npos) {
|
||||
flagStr = strInput.substr(pos + 1, string::npos);
|
||||
std::string flagStr;
|
||||
if (pos != std::string::npos) {
|
||||
flagStr = strInput.substr(pos + 1, std::string::npos);
|
||||
}
|
||||
if (flagStr.size() > 0)
|
||||
if (!findSighashFlags(nHashType, flagStr))
|
||||
throw runtime_error("unknown sighash flag/sign option");
|
||||
throw std::runtime_error("unknown sighash flag/sign option");
|
||||
|
||||
vector<CTransaction> txVariants;
|
||||
std::vector<CTransaction> txVariants;
|
||||
txVariants.push_back(tx);
|
||||
|
||||
// mergedTx will end up with all the signatures; it
|
||||
@@ -393,7 +406,7 @@ static void MutateTxSign(CMutableTransaction& tx, const string& strInput)
|
||||
CCoinsViewCache view(&viewDummy);
|
||||
|
||||
if (!registers.count("privatekeys"))
|
||||
throw runtime_error("privatekeys register variable must be set.");
|
||||
throw std::runtime_error("privatekeys register variable must be set.");
|
||||
bool fGivenKeys = false;
|
||||
CBasicKeyStore tempKeystore;
|
||||
UniValue keysObj = registers["privatekeys"];
|
||||
@@ -401,46 +414,44 @@ static void MutateTxSign(CMutableTransaction& tx, const string& strInput)
|
||||
|
||||
for (size_t kidx = 0; kidx < keysObj.size(); kidx++) {
|
||||
if (!keysObj[kidx].isStr())
|
||||
throw runtime_error("privatekey not a string");
|
||||
CBitcoinSecret vchSecret;
|
||||
bool fGood = vchSecret.SetString(keysObj[kidx].getValStr());
|
||||
if (!fGood)
|
||||
throw runtime_error("privatekey not valid");
|
||||
|
||||
CKey key = vchSecret.GetKey();
|
||||
throw std::runtime_error("privatekey not a std::string");
|
||||
CKey key = DecodeSecret(keysObj[kidx].getValStr());
|
||||
if (!key.IsValid()) {
|
||||
throw std::runtime_error("privatekey not valid");
|
||||
}
|
||||
tempKeystore.AddKey(key);
|
||||
}
|
||||
|
||||
// Add previous txouts given in the RPC call:
|
||||
if (!registers.count("prevtxs"))
|
||||
throw runtime_error("prevtxs register variable must be set.");
|
||||
throw std::runtime_error("prevtxs register variable must be set.");
|
||||
UniValue prevtxsObj = registers["prevtxs"];
|
||||
{
|
||||
for (size_t previdx = 0; previdx < prevtxsObj.size(); previdx++) {
|
||||
UniValue prevOut = prevtxsObj[previdx];
|
||||
if (!prevOut.isObject())
|
||||
throw runtime_error("expected prevtxs internal object");
|
||||
throw std::runtime_error("expected prevtxs internal object");
|
||||
|
||||
map<string,UniValue::VType> types = boost::assign::map_list_of("txid", UniValue::VSTR)("vout",UniValue::VNUM)("scriptPubKey",UniValue::VSTR);
|
||||
std::map<std::string,UniValue::VType> types = boost::assign::map_list_of("txid", UniValue::VSTR)("vout",UniValue::VNUM)("scriptPubKey",UniValue::VSTR);
|
||||
if (!prevOut.checkObject(types))
|
||||
throw runtime_error("prevtxs internal object typecheck fail");
|
||||
throw std::runtime_error("prevtxs internal object typecheck fail");
|
||||
|
||||
uint256 txid = ParseHashUV(prevOut["txid"], "txid");
|
||||
|
||||
int nOut = atoi(prevOut["vout"].getValStr());
|
||||
if (nOut < 0)
|
||||
throw runtime_error("vout must be positive");
|
||||
throw std::runtime_error("vout must be positive");
|
||||
|
||||
vector<unsigned char> pkData(ParseHexUV(prevOut["scriptPubKey"], "scriptPubKey"));
|
||||
std::vector<unsigned char> pkData(ParseHexUV(prevOut["scriptPubKey"], "scriptPubKey"));
|
||||
CScript scriptPubKey(pkData.begin(), pkData.end());
|
||||
|
||||
{
|
||||
CCoinsModifier coins = view.ModifyCoins(txid);
|
||||
if (coins->IsAvailable(nOut) && coins->vout[nOut].scriptPubKey != scriptPubKey) {
|
||||
string err("Previous output scriptPubKey mismatch:\n");
|
||||
err = err + coins->vout[nOut].scriptPubKey.ToString() + "\nvs:\n"+
|
||||
scriptPubKey.ToString();
|
||||
throw runtime_error(err);
|
||||
std::string err("Previous output scriptPubKey mismatch:\n");
|
||||
err = err + ScriptToAsmStr(coins->vout[nOut].scriptPubKey) + "\nvs:\n"+
|
||||
ScriptToAsmStr(scriptPubKey);
|
||||
throw std::runtime_error(err);
|
||||
}
|
||||
if ((unsigned int)nOut >= coins->vout.size())
|
||||
coins->vout.resize(nOut+1);
|
||||
@@ -456,7 +467,7 @@ static void MutateTxSign(CMutableTransaction& tx, const string& strInput)
|
||||
if (fGivenKeys && scriptPubKey.IsPayToScriptHash() &&
|
||||
prevOut.exists("redeemScript")) {
|
||||
UniValue v = prevOut["redeemScript"];
|
||||
vector<unsigned char> rsData(ParseHexUV(v, "redeemScript"));
|
||||
std::vector<unsigned char> rsData(ParseHexUV(v, "redeemScript"));
|
||||
CScript redeemScript(rsData.begin(), rsData.end());
|
||||
tempKeystore.AddCScript(redeemScript);
|
||||
}
|
||||
@@ -516,8 +527,8 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
static void MutateTx(CMutableTransaction& tx, const string& command,
|
||||
const string& commandVal)
|
||||
static void MutateTx(CMutableTransaction& tx, const std::string& command,
|
||||
const std::string& commandVal)
|
||||
{
|
||||
boost::scoped_ptr<Secp256k1Init> ecc;
|
||||
|
||||
@@ -552,7 +563,7 @@ static void MutateTx(CMutableTransaction& tx, const string& command,
|
||||
RegisterSet(commandVal);
|
||||
|
||||
else
|
||||
throw runtime_error("unknown command");
|
||||
throw std::runtime_error("unknown command");
|
||||
}
|
||||
|
||||
static void OutputTxJSON(const CTransaction& tx)
|
||||
@@ -560,20 +571,20 @@ static void OutputTxJSON(const CTransaction& tx)
|
||||
UniValue entry(UniValue::VOBJ);
|
||||
TxToUniv(tx, uint256(), entry);
|
||||
|
||||
string jsonOutput = entry.write(4);
|
||||
std::string jsonOutput = entry.write(4);
|
||||
fprintf(stdout, "%s\n", jsonOutput.c_str());
|
||||
}
|
||||
|
||||
static void OutputTxHash(const CTransaction& tx)
|
||||
{
|
||||
string strHexHash = tx.GetHash().GetHex(); // the hex-encoded transaction hash (aka the transaction id)
|
||||
std::string strHexHash = tx.GetHash().GetHex(); // the hex-encoded transaction hash (aka the transaction id)
|
||||
|
||||
fprintf(stdout, "%s\n", strHexHash.c_str());
|
||||
}
|
||||
|
||||
static void OutputTxHex(const CTransaction& tx)
|
||||
{
|
||||
string strHex = EncodeHexTx(tx);
|
||||
std::string strHex = EncodeHexTx(tx);
|
||||
|
||||
fprintf(stdout, "%s\n", strHex.c_str());
|
||||
}
|
||||
@@ -588,10 +599,10 @@ static void OutputTx(const CTransaction& tx)
|
||||
OutputTxHex(tx);
|
||||
}
|
||||
|
||||
static string readStdin()
|
||||
static std::string readStdin()
|
||||
{
|
||||
char buf[4096];
|
||||
string ret;
|
||||
std::string ret;
|
||||
|
||||
while (!feof(stdin)) {
|
||||
size_t bread = fread(buf, 1, sizeof(buf), stdin);
|
||||
@@ -601,7 +612,7 @@ static string readStdin()
|
||||
}
|
||||
|
||||
if (ferror(stdin))
|
||||
throw runtime_error("error reading stdin");
|
||||
throw std::runtime_error("error reading stdin");
|
||||
|
||||
boost::algorithm::trim_right(ret);
|
||||
|
||||
@@ -610,7 +621,7 @@ static string readStdin()
|
||||
|
||||
static int CommandLineRawTx(int argc, char* argv[])
|
||||
{
|
||||
string strPrint;
|
||||
std::string strPrint;
|
||||
int nRet = 0;
|
||||
try {
|
||||
// Skip switches; Permit common stdin convention "-"
|
||||
@@ -626,15 +637,15 @@ static int CommandLineRawTx(int argc, char* argv[])
|
||||
if (!fCreateBlank) {
|
||||
// require at least one param
|
||||
if (argc < 2)
|
||||
throw runtime_error("too few parameters");
|
||||
throw std::runtime_error("too few parameters");
|
||||
|
||||
// param: hex-encoded bitcoin transaction
|
||||
string strHexTx(argv[1]);
|
||||
std::string strHexTx(argv[1]);
|
||||
if (strHexTx == "-") // "-" implies standard input
|
||||
strHexTx = readStdin();
|
||||
|
||||
if (!DecodeHexTx(txDecodeTmp, strHexTx))
|
||||
throw runtime_error("invalid transaction encoding");
|
||||
throw std::runtime_error("invalid transaction encoding");
|
||||
|
||||
startArg = 2;
|
||||
} else
|
||||
@@ -643,10 +654,10 @@ static int CommandLineRawTx(int argc, char* argv[])
|
||||
CMutableTransaction tx(txDecodeTmp);
|
||||
|
||||
for (int i = startArg; i < argc; i++) {
|
||||
string arg = argv[i];
|
||||
string key, value;
|
||||
std::string arg = argv[i];
|
||||
std::string key, value;
|
||||
size_t eqpos = arg.find('=');
|
||||
if (eqpos == string::npos)
|
||||
if (eqpos == std::string::npos)
|
||||
key = arg;
|
||||
else {
|
||||
key = arg.substr(0, eqpos);
|
||||
@@ -663,7 +674,7 @@ static int CommandLineRawTx(int argc, char* argv[])
|
||||
throw;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
strPrint = string("error: ") + e.what();
|
||||
strPrint = std::string("error: ") + e.what();
|
||||
nRet = EXIT_FAILURE;
|
||||
}
|
||||
catch (...) {
|
||||
@@ -682,8 +693,9 @@ int main(int argc, char* argv[])
|
||||
SetupEnvironment();
|
||||
|
||||
try {
|
||||
if(!AppInitRawTx(argc, argv))
|
||||
return EXIT_FAILURE;
|
||||
int ret = AppInitRawTx(argc, argv);
|
||||
if (ret != CONTINUE_EXECUTION)
|
||||
return ret;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
PrintExceptionContinue(&e, "AppInitRawTx()");
|
||||
|
||||
10
src/komodo.h
10
src/komodo.h
@@ -885,7 +885,7 @@ void komodo_connectblock(CBlockIndex *pindex,CBlock& block)
|
||||
{
|
||||
/*if ( i == 0 && j == 0 )
|
||||
{
|
||||
uint8_t *script = (uint8_t *)block.vtx[0].vout[numvouts-1].scriptPubKey.data();
|
||||
uint8_t *script = (uint8_t *)&block.vtx[0].vout[numvouts-1].scriptPubKey[0];
|
||||
if ( numvouts <= 2 || script[0] != 0x6a )
|
||||
{
|
||||
if ( numvouts == 2 && block.vtx[0].vout[1].nValue != 0 )
|
||||
@@ -902,11 +902,7 @@ void komodo_connectblock(CBlockIndex *pindex,CBlock& block)
|
||||
len = block.vtx[i].vout[j].scriptPubKey.size();
|
||||
if ( len >= sizeof(uint32_t) && len <= sizeof(scriptbuf) )
|
||||
{
|
||||
#ifdef KOMODO_ZCASH
|
||||
memcpy(scriptbuf,block.vtx[i].vout[j].scriptPubKey.data(),len);
|
||||
#else
|
||||
memcpy(scriptbuf,(uint8_t *)&block.vtx[i].vout[j].scriptPubKey[0],len);
|
||||
#endif
|
||||
notaryid = komodo_voutupdate(&isratification,notaryid,scriptbuf,len,height,txhash,i,j,&voutmask,&specialtx,¬arizedheight,(uint64_t)block.vtx[i].vout[j].nValue,notarized,signedmask,(uint32_t)chainActive.LastTip()->GetBlockTime());
|
||||
if ( 0 && i > 0 )
|
||||
{
|
||||
@@ -936,11 +932,7 @@ void komodo_connectblock(CBlockIndex *pindex,CBlock& block)
|
||||
len = block.vtx[i].vout[j].scriptPubKey.size();
|
||||
if ( len >= sizeof(uint32_t) && len <= sizeof(scriptbuf) )
|
||||
{
|
||||
#ifdef KOMODO_ZCASH
|
||||
memcpy(scriptbuf,block.vtx[i].vout[j].scriptPubKey.data(),len);
|
||||
#else
|
||||
memcpy(scriptbuf,(uint8_t *)&block.vtx[i].vout[j].scriptPubKey[0],len);
|
||||
#endif
|
||||
if ( len == 35 && scriptbuf[0] == 33 && scriptbuf[34] == 0xac )
|
||||
{
|
||||
memcpy(pubkeys[numvalid++],scriptbuf+1,33);
|
||||
|
||||
@@ -15,14 +15,10 @@
|
||||
|
||||
// komodo functions that interact with bitcoind C++
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <curl/curl.h>
|
||||
#include <curl/easy.h>
|
||||
#else
|
||||
#include <curl/curl.h>
|
||||
#include <curl/easy.h>
|
||||
#endif
|
||||
|
||||
#include "primitives/nonce.h"
|
||||
#include "consensus/params.h"
|
||||
#include "komodo_defs.h"
|
||||
|
||||
int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp);
|
||||
@@ -648,11 +644,7 @@ int32_t komodo_is_notarytx(const CTransaction& tx)
|
||||
uint8_t *ptr; static uint8_t crypto777[33];
|
||||
if ( tx.vout.size() > 0 )
|
||||
{
|
||||
#ifdef KOMODO_ZCASH
|
||||
ptr = (uint8_t *)tx.vout[0].scriptPubKey.data();
|
||||
#else
|
||||
ptr = (uint8_t *)&tx.vout[0].scriptPubKey[0];
|
||||
#endif
|
||||
if ( ptr != 0 )
|
||||
{
|
||||
if ( crypto777[0] == 0 )
|
||||
@@ -679,11 +671,7 @@ int32_t komodo_block2height(CBlock *block)
|
||||
}
|
||||
if ( block != 0 && block->vtx[0].vin.size() > 0 )
|
||||
{
|
||||
#ifdef KOMODO_ZCASH
|
||||
ptr = (uint8_t *)block->vtx[0].vin[0].scriptSig.data();
|
||||
#else
|
||||
ptr = (uint8_t *)&block->vtx[0].vin[0].scriptSig[0];
|
||||
#endif
|
||||
if ( ptr != 0 && block->vtx[0].vin[0].scriptSig.size() > 5 )
|
||||
{
|
||||
//for (i=0; i<6; i++)
|
||||
@@ -1473,6 +1461,7 @@ bool verusCheckPOSBlock(int32_t slowflag, CBlock *pblock, int32_t height)
|
||||
uint256 txid, blkHash;
|
||||
int32_t txn_count;
|
||||
uint32_t voutNum;
|
||||
CAmount value;
|
||||
bool isPOS = false;
|
||||
CTxDestination voutaddress, destaddress, cbaddress;
|
||||
arith_uint256 target, hash;
|
||||
@@ -1481,8 +1470,8 @@ bool verusCheckPOSBlock(int32_t slowflag, CBlock *pblock, int32_t height)
|
||||
if (!pblock->IsVerusPOSBlock())
|
||||
{
|
||||
printf("%s, height %d not POS block\n", pblock->nNonce.GetHex().c_str(), height);
|
||||
pblock->nNonce.SetPOSTarget(pblock->nNonce.GetPOSTarget());
|
||||
printf("%s after setting POS target\n", pblock->nNonce.GetHex().c_str());
|
||||
//pblock->nNonce.SetPOSTarget(pblock->nNonce.GetPOSTarget());
|
||||
//printf("%s after setting POS target\n", pblock->nNonce.GetHex().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1495,90 +1484,128 @@ bool verusCheckPOSBlock(int32_t slowflag, CBlock *pblock, int32_t height)
|
||||
target.SetCompact(pblock->GetVerusPOSTarget());
|
||||
txid = pblock->vtx[txn_count-1].vin[0].prevout.hash;
|
||||
voutNum = pblock->vtx[txn_count-1].vin[0].prevout.n;
|
||||
value = pblock->vtx[txn_count-1].vout[0].nValue;
|
||||
|
||||
#ifndef KOMODO_ZCASH
|
||||
if (!GetTransaction(txid, tx, Params().GetConsensus(), blkHash, true))
|
||||
#else
|
||||
if (!GetTransaction(txid, tx, blkHash, true))
|
||||
#endif
|
||||
{
|
||||
fprintf(stderr,"ERROR: invalid PoS block %s - no transaction\n",blkHash.ToString().c_str());
|
||||
}
|
||||
else if (!(pastBlockIndex = komodo_chainactive(height - 100)))
|
||||
{
|
||||
fprintf(stderr,"WARNING: chain not fully loaded or invalid PoS block %s - no past block found\n",blkHash.ToString().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
CBlockHeader bh = pastBlockIndex->GetBlockHeader();
|
||||
uint256 pastHash = bh.GetVerusEntropyHash(height - 100);
|
||||
|
||||
// if height is over when Nonce is required to be the new format, we check that the new format is correct
|
||||
// if over when we have the new POS hash function, we validate that as well
|
||||
// they are 100 blocks apart
|
||||
CPOSNonce nonce = pblock->nNonce;
|
||||
|
||||
hash = UintToArith256(tx.GetVerusPOSHash(&nonce, voutNum, height, pastHash));
|
||||
if (hash <= target)
|
||||
bool validHash;
|
||||
bool enablePOSNonce = CPOSNonce::NewPOSActive(height);
|
||||
bool newPOSActive = enablePOSNonce || (Params().GetConsensus().vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight <= height);
|
||||
uint256 rawHash;
|
||||
arith_uint256 posHash;
|
||||
if (newPOSActive)
|
||||
{
|
||||
if ((mapBlockIndex.count(blkHash) == 0) ||
|
||||
!(pastBlockIndex = mapBlockIndex[blkHash]) ||
|
||||
(height - pastBlockIndex->nHeight) < VERUS_MIN_STAKEAGE)
|
||||
validHash = pblock->GetRawVerusPOSHash(rawHash, height);
|
||||
posHash = UintToArith256(rawHash) / value;
|
||||
//if (slowflag)
|
||||
//{
|
||||
// printf("first nNonce: %s, height: %d\n", pblock->nNonce.GetHex().c_str(), height);
|
||||
// printf("Raw POShash: %s\n", posHash.GetHex().c_str());
|
||||
//}
|
||||
}
|
||||
if (newPOSActive && !(validHash && posHash <= target))
|
||||
{
|
||||
printf("ERROR: invalid nonce value for PoS block\nnNonce: %s\nrawHash: %s\nposHash: %s\nvalue: %ull\n",
|
||||
pblock->nNonce.GetHex().c_str(), rawHash.GetHex().c_str(), posHash.GetHex().c_str(), value);
|
||||
}
|
||||
//else
|
||||
{
|
||||
if (slowflag == 0)
|
||||
{
|
||||
fprintf(stderr,"ERROR: invalid PoS block %s - stake transaction too new\n",blkHash.ToString().c_str());
|
||||
isPOS = true;
|
||||
}
|
||||
else if ( slowflag != 0 )
|
||||
else if (!(pastBlockIndex = komodo_chainactive(height - 100)))
|
||||
{
|
||||
// make sure we have the right target
|
||||
CBlockIndex *previndex;
|
||||
if (!(previndex = mapBlockIndex[pblock->hashPrevBlock]))
|
||||
{
|
||||
fprintf(stderr,"ERROR: invalid PoS block %s - no prev block found\n",blkHash.ToString().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
arith_uint256 cTarget;
|
||||
uint32_t nBits = lwmaGetNextPOSRequired(previndex, Params().GetConsensus());
|
||||
cTarget.SetCompact(nBits);
|
||||
bool nonceOK = true;
|
||||
|
||||
if (CPOSNonce::NewNonceActive(height) && !nonce.CheckPOSEntropy(pastHash, txid, voutNum))
|
||||
{
|
||||
fprintf(stderr,"ERROR: invalid PoS block %s - nonce entropy corrupted or forged\n",blkHash.ToString().c_str());
|
||||
nonceOK = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cTarget != target)
|
||||
{
|
||||
fprintf(stderr,"ERROR: invalid PoS block %s - invalid diff target\n",blkHash.ToString().c_str());
|
||||
nonceOK = false;
|
||||
}
|
||||
}
|
||||
if ( nonceOK && ExtractDestination(pblock->vtx[txn_count-1].vout[0].scriptPubKey, voutaddress) &&
|
||||
ExtractDestination(tx.vout[voutNum].scriptPubKey, destaddress) &&
|
||||
CScriptExt::ExtractVoutDestination(pblock->vtx[0], 0, cbaddress) )
|
||||
{
|
||||
strcpy(voutaddr, CBitcoinAddress(voutaddress).ToString().c_str());
|
||||
strcpy(destaddr, CBitcoinAddress(destaddress).ToString().c_str());
|
||||
strcpy(cbaddr, CBitcoinAddress(cbaddress).ToString().c_str());
|
||||
if ( !strcmp(destaddr,voutaddr) && ( !strcmp(destaddr,cbaddr) || (height < 17840)) )
|
||||
{
|
||||
isPOS = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr,"ERROR: invalid PoS block %s - invalid stake or coinbase destination\n",blkHash.ToString().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
fprintf(stderr,"ERROR: chain not fully loaded or invalid PoS block %s - no past block found\n",blkHash.ToString().c_str());
|
||||
}
|
||||
else
|
||||
#ifndef KOMODO_ZCASH
|
||||
if (!GetTransaction(txid, tx, Params().GetConsensus(), blkHash, true))
|
||||
#else
|
||||
if (!GetTransaction(txid, tx, blkHash, true))
|
||||
#endif
|
||||
{
|
||||
fprintf(stderr,"ERROR: invalid PoS block %s - no source transaction\n",blkHash.ToString().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
// with fast check, we get true here, slow check ensures destination address
|
||||
// of staking transaction matches original source and that the target
|
||||
// matches the consensus target
|
||||
isPOS = true;
|
||||
CBlockHeader bh = pastBlockIndex->GetBlockHeader();
|
||||
uint256 pastHash = bh.GetVerusEntropyHash(height - 100);
|
||||
|
||||
// if height is over when Nonce is required to be the new format, we check that the new format is correct
|
||||
// if over when we have the new POS hash function, we validate that as well
|
||||
// they are 100 blocks apart
|
||||
CPOSNonce nonce = pblock->nNonce;
|
||||
|
||||
//printf("before nNonce: %s, height: %d\n", pblock->nNonce.GetHex().c_str(), height);
|
||||
//validHash = pblock->GetRawVerusPOSHash(rawHash, height);
|
||||
//hash = UintToArith256(rawHash) / tx.vout[voutNum].nValue;
|
||||
//printf("Raw POShash: %s\n", hash.GetHex().c_str());
|
||||
|
||||
hash = UintToArith256(tx.GetVerusPOSHash(&nonce, voutNum, height, pastHash));
|
||||
|
||||
//printf("after nNonce: %s, height: %d\n", nonce.GetHex().c_str(), height);
|
||||
//printf("POShash: %s\n\n", hash.GetHex().c_str());
|
||||
|
||||
//if ((!newPOSActive || posHash == hash) && hash <= target)
|
||||
if (hash <= target)
|
||||
{
|
||||
if ((mapBlockIndex.count(blkHash) == 0) ||
|
||||
!(pastBlockIndex = mapBlockIndex[blkHash]) ||
|
||||
(height - pastBlockIndex->nHeight) < VERUS_MIN_STAKEAGE)
|
||||
{
|
||||
fprintf(stderr,"ERROR: invalid PoS block %s - stake transaction too new\n",blkHash.ToString().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
// make sure we have the right target
|
||||
CBlockIndex *previndex;
|
||||
if (!(previndex = mapBlockIndex[pblock->hashPrevBlock]))
|
||||
{
|
||||
fprintf(stderr,"ERROR: invalid PoS block %s - no prev block found\n",blkHash.ToString().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
arith_uint256 cTarget;
|
||||
uint32_t nBits = lwmaGetNextPOSRequired(previndex, Params().GetConsensus());
|
||||
cTarget.SetCompact(nBits);
|
||||
bool nonceOK = true;
|
||||
|
||||
// check to see how many fail
|
||||
//if (nonce != pblock->nNonce)
|
||||
// printf("Mismatched nNonce: %s\nblkHash: %s, height: %d\n", nonce.GetHex().c_str(), pblock->GetHash().GetHex().c_str(), height);
|
||||
|
||||
if (CPOSNonce::NewNonceActive(height) && !nonce.CheckPOSEntropy(pastHash, txid, voutNum))
|
||||
{
|
||||
fprintf(stderr,"ERROR: invalid PoS block %s - nonce entropy corrupted or forged\n",blkHash.ToString().c_str());
|
||||
nonceOK = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cTarget != target)
|
||||
{
|
||||
fprintf(stderr,"ERROR: invalid PoS block %s - invalid diff target\n",blkHash.ToString().c_str());
|
||||
nonceOK = false;
|
||||
}
|
||||
}
|
||||
if ( nonceOK && ExtractDestination(pblock->vtx[txn_count-1].vout[0].scriptPubKey, voutaddress) &&
|
||||
ExtractDestination(tx.vout[voutNum].scriptPubKey, destaddress) &&
|
||||
CScriptExt::ExtractVoutDestination(pblock->vtx[0], 0, cbaddress) )
|
||||
{
|
||||
strcpy(voutaddr, CBitcoinAddress(voutaddress).ToString().c_str());
|
||||
strcpy(destaddr, CBitcoinAddress(destaddress).ToString().c_str());
|
||||
strcpy(cbaddr, CBitcoinAddress(cbaddress).ToString().c_str());
|
||||
if ( !strcmp(destaddr,voutaddr) && ( !strcmp(destaddr,cbaddr) || (height < 17840)) )
|
||||
{
|
||||
isPOS = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr,"ERROR: invalid PoS block %s - invalid stake or coinbase destination\n",blkHash.ToString().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1596,7 +1623,7 @@ int64_t komodo_checkcommission(CBlock *pblock,int32_t height)
|
||||
return(-1);
|
||||
else if ( checktoshis != 0 )
|
||||
{
|
||||
script = (uint8_t *)pblock->vtx[0].vout[1].scriptPubKey.data();
|
||||
script = (uint8_t *)&pblock->vtx[0].vout[1].scriptPubKey[0];
|
||||
if ( script[0] != 33 || script[34] != OP_CHECKSIG || memcmp(script+1,ASSETCHAINS_OVERRIDE_PUBKEY33,33) != 0 )
|
||||
return(-1);
|
||||
if ( pblock->vtx[0].vout[1].nValue != checktoshis )
|
||||
@@ -1702,7 +1729,7 @@ int32_t komodo_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height)
|
||||
{
|
||||
if ( height == 1 )
|
||||
{
|
||||
script = (uint8_t *)pblock->vtx[0].vout[0].scriptPubKey.data();
|
||||
script = (uint8_t *)&pblock->vtx[0].vout[0].scriptPubKey[0];
|
||||
if ( script[0] != 33 || script[34] != OP_CHECKSIG || memcmp(script+1,ASSETCHAINS_OVERRIDE_PUBKEY33,33) != 0 )
|
||||
return(-1);
|
||||
}
|
||||
@@ -1725,7 +1752,7 @@ int64_t komodo_newcoins(int64_t *zfundsp,int32_t nHeight,CBlock *pblock)
|
||||
for (i=0; i<n; i++)
|
||||
{
|
||||
CTransaction vintx,&tx = pblock->vtx[i];
|
||||
zfunds += (tx.GetJoinSplitValueOut() - tx.GetJoinSplitValueIn());
|
||||
zfunds += (tx.GetValueOut() - tx.GetShieldedValueIn());
|
||||
if ( (m= tx.vin.size()) > 0 )
|
||||
{
|
||||
for (j=0; j<m; j++)
|
||||
@@ -1750,7 +1777,7 @@ int64_t komodo_newcoins(int64_t *zfundsp,int32_t nHeight,CBlock *pblock)
|
||||
voutsum += tx.vout[j].nValue;
|
||||
else printf("skip %.8f -> %s\n",dstr(tx.vout[j].nValue),CBitcoinAddress(address).ToString().c_str());
|
||||
}
|
||||
script = (uint8_t *)tx.vout[j].scriptPubKey.data();
|
||||
script = (uint8_t *)&tx.vout[j].scriptPubKey[0];
|
||||
if ( script == 0 || script[0] != 0x6a )
|
||||
{
|
||||
if ( ExtractDestination(tx.vout[j].scriptPubKey,address) != 0 && strcmp("RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPVMY",CBitcoinAddress(address).ToString().c_str()) != 0 )
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#define IGUANA_MAXSCRIPTSIZE 10001
|
||||
#define KOMODO_MAXMEMPOOLTIME 3600 // affects consensus
|
||||
#define CRYPTO777_PUBSECPSTR "020e46e79a2a8d12b9b5d12c7a91adb4e454edfae43c0a0cb805427d2ac7613fd9"
|
||||
#define VRSC_KMD_MERGE_FIX 227520 // height that the inadvertent KMD merge/coinbase output changes to VRSC are removed
|
||||
#define VRSC_SAPLING_UPGRADE 227520 // height that the inadvertent KMD merge/coinbase output changes to VRSC are removed
|
||||
// approximately October 28th
|
||||
|
||||
#endif
|
||||
|
||||
@@ -690,14 +690,14 @@ int32_t komodo_check_deposit(int32_t height,const CBlock& block,uint32_t prevtim
|
||||
// we don't want any of these checks in VRSC, leave it for other chains until/unless KMD removes
|
||||
if ( ASSETCHAINS_SYMBOL[0] == 0 ||
|
||||
(ASSETCHAINS_COMMISSION != 0 && height > 1) ||
|
||||
(strcmp(ASSETCHAINS_SYMBOL,"VRSC") == 0 && height < VRSC_KMD_MERGE_FIX) )
|
||||
(strcmp(ASSETCHAINS_SYMBOL,"VRSC") == 0 && height < VRSC_SAPLING_UPGRADE) )
|
||||
{
|
||||
n = block.vtx[0].vout.size();
|
||||
int64_t val,prevtotal = 0; int32_t strangeout=0,overflow = 0;
|
||||
total = 0;
|
||||
for (i=1; i<n; i++)
|
||||
{
|
||||
script = (uint8_t *)block.vtx[0].vout[i].scriptPubKey.data();
|
||||
script = (uint8_t *)&block.vtx[0].vout[i].scriptPubKey[0];
|
||||
if ( (val= block.vtx[0].vout[i].nValue) < 0 || val >= MAX_MONEY )
|
||||
{
|
||||
overflow = 1;
|
||||
@@ -739,7 +739,7 @@ int32_t komodo_check_deposit(int32_t height,const CBlock& block,uint32_t prevtim
|
||||
}
|
||||
else if ( height > 814000 )
|
||||
{
|
||||
script = (uint8_t *)block.vtx[0].vout[0].scriptPubKey.data();
|
||||
script = (uint8_t *)&block.vtx[0].vout[0].scriptPubKey[0];
|
||||
return(-1 * (komodo_electednotary(&num,script+1,height,0) >= 0) * (height > 1000000));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,8 @@ uint32_t ASSETCHAIN_INIT,ASSETCHAINS_CC,KOMODO_STOPAT;
|
||||
uint32_t ASSETCHAINS_MAGIC = 2387029918;
|
||||
int64_t ASSETCHAINS_GENESISTXVAL = 5000000000;
|
||||
|
||||
int64_t MAX_MONEY = 200000000 * 100000000LL;
|
||||
|
||||
// consensus variables for coinbase timelock control and timelock transaction support
|
||||
// time locks are specified enough to enable their use initially to lock specific coinbase transactions for emission control
|
||||
// to be verifiable, timelocks require additional data that enables them to be validated and their ownership and
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
#define KOMODO_ENDOFERA 7777777
|
||||
#define KOMODO_INTEREST ((uint64_t)5000000) //((uint64_t)(0.05 * COIN)) // 5%
|
||||
int64_t MAX_MONEY = 200000000 * 100000000LL;
|
||||
extern int64_t MAX_MONEY;
|
||||
extern uint8_t NOTARY_PUBKEY33[];
|
||||
|
||||
#ifdef notanymore
|
||||
|
||||
@@ -1643,9 +1643,10 @@ uint64_t komodo_ac_block_subsidy(int nHeight)
|
||||
return(subsidy);
|
||||
}
|
||||
|
||||
extern int64_t MAX_MONEY;
|
||||
|
||||
void komodo_args(char *argv0)
|
||||
{
|
||||
extern int64_t MAX_MONEY;
|
||||
extern const char *Notaries_elected1[][2];
|
||||
std::string name,addn; char *dirname,fname[512],arg0str[64],magicstr[9]; uint8_t magic[4],extrabuf[256],*extraptr=0; FILE *fp; uint64_t val; uint16_t port; int32_t i,baseid,len,n,extralen = 0;
|
||||
IS_KOMODO_NOTARY = GetBoolArg("-notary", false);
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
// Copyright (c) 2012-2014 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef BITCOIN_LEVELDBWRAPPER_H
|
||||
#define BITCOIN_LEVELDBWRAPPER_H
|
||||
|
||||
#include "clientversion.h"
|
||||
#include "serialize.h"
|
||||
#include "streams.h"
|
||||
#include "util.h"
|
||||
#include "version.h"
|
||||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
#include <leveldb/db.h>
|
||||
#include <leveldb/write_batch.h>
|
||||
|
||||
class leveldb_error : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
leveldb_error(const std::string& msg) : std::runtime_error(msg) {}
|
||||
};
|
||||
|
||||
void HandleError(const leveldb::Status& status);
|
||||
|
||||
/** Batch of changes queued to be written to a CLevelDBWrapper */
|
||||
class CLevelDBBatch
|
||||
{
|
||||
friend class CLevelDBWrapper;
|
||||
|
||||
private:
|
||||
leveldb::WriteBatch batch;
|
||||
|
||||
public:
|
||||
template <typename K, typename V>
|
||||
void Write(const K& key, const V& value)
|
||||
{
|
||||
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
|
||||
ssKey.reserve(ssKey.GetSerializeSize(key));
|
||||
ssKey << key;
|
||||
leveldb::Slice slKey(&ssKey[0], ssKey.size());
|
||||
|
||||
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
|
||||
ssValue.reserve(ssValue.GetSerializeSize(value));
|
||||
ssValue << value;
|
||||
leveldb::Slice slValue(&ssValue[0], ssValue.size());
|
||||
|
||||
batch.Put(slKey, slValue);
|
||||
}
|
||||
|
||||
template <typename K>
|
||||
void Erase(const K& key)
|
||||
{
|
||||
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
|
||||
ssKey.reserve(ssKey.GetSerializeSize(key));
|
||||
ssKey << key;
|
||||
leveldb::Slice slKey(&ssKey[0], ssKey.size());
|
||||
|
||||
batch.Delete(slKey);
|
||||
}
|
||||
};
|
||||
|
||||
class CLevelDBWrapper
|
||||
{
|
||||
private:
|
||||
//! custom environment this database is using (may be NULL in case of default environment)
|
||||
leveldb::Env* penv;
|
||||
|
||||
//! database options used
|
||||
leveldb::Options options;
|
||||
|
||||
//! options used when reading from the database
|
||||
leveldb::ReadOptions readoptions;
|
||||
|
||||
//! options used when iterating over values of the database
|
||||
leveldb::ReadOptions iteroptions;
|
||||
|
||||
//! options used when writing to the database
|
||||
leveldb::WriteOptions writeoptions;
|
||||
|
||||
//! options used when sync writing to the database
|
||||
leveldb::WriteOptions syncoptions;
|
||||
|
||||
//! the database itself
|
||||
leveldb::DB* pdb;
|
||||
|
||||
public:
|
||||
CLevelDBWrapper(const boost::filesystem::path& path, size_t nCacheSize, bool fMemory = false, bool fWipe = false, bool compression = false, int maxOpenFiles = 64);
|
||||
~CLevelDBWrapper();
|
||||
|
||||
template <typename K, typename V>
|
||||
bool Read(const K& key, V& value) const
|
||||
{
|
||||
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
|
||||
ssKey.reserve(ssKey.GetSerializeSize(key));
|
||||
ssKey << key;
|
||||
leveldb::Slice slKey(&ssKey[0], ssKey.size());
|
||||
|
||||
std::string strValue;
|
||||
leveldb::Status status = pdb->Get(readoptions, slKey, &strValue);
|
||||
if (!status.ok()) {
|
||||
if (status.IsNotFound())
|
||||
return false;
|
||||
LogPrintf("LevelDB read failure: %s\n", status.ToString());
|
||||
HandleError(status);
|
||||
}
|
||||
try {
|
||||
CDataStream ssValue(strValue.data(), strValue.data() + strValue.size(), SER_DISK, CLIENT_VERSION);
|
||||
ssValue >> value;
|
||||
} catch (const std::exception&) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename K, typename V>
|
||||
bool Write(const K& key, const V& value, bool fSync = false)
|
||||
{
|
||||
CLevelDBBatch batch;
|
||||
batch.Write(key, value);
|
||||
return WriteBatch(batch, fSync);
|
||||
}
|
||||
|
||||
template <typename K>
|
||||
bool Exists(const K& key) const
|
||||
{
|
||||
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
|
||||
ssKey.reserve(ssKey.GetSerializeSize(key));
|
||||
ssKey << key;
|
||||
leveldb::Slice slKey(&ssKey[0], ssKey.size());
|
||||
|
||||
std::string strValue;
|
||||
leveldb::Status status = pdb->Get(readoptions, slKey, &strValue);
|
||||
if (!status.ok()) {
|
||||
if (status.IsNotFound())
|
||||
return false;
|
||||
LogPrintf("LevelDB read failure: %s\n", status.ToString());
|
||||
HandleError(status);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename K>
|
||||
bool Erase(const K& key, bool fSync = false)
|
||||
{
|
||||
CLevelDBBatch batch;
|
||||
batch.Erase(key);
|
||||
return WriteBatch(batch, fSync);
|
||||
}
|
||||
|
||||
bool WriteBatch(CLevelDBBatch& batch, bool fSync = false);
|
||||
|
||||
// not available for LevelDB; provide for compatibility with BDB
|
||||
bool Flush()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Sync()
|
||||
{
|
||||
CLevelDBBatch batch;
|
||||
return WriteBatch(batch, true);
|
||||
}
|
||||
|
||||
// not exactly clean encapsulation, but it's easiest for now
|
||||
leveldb::Iterator* NewIterator()
|
||||
{
|
||||
return pdb->NewIterator(iteroptions);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // BITCOIN_LEVELDBWRAPPER_H
|
||||
620
src/main.cpp
620
src/main.cpp
File diff suppressed because it is too large
Load Diff
81
src/main.h
81
src/main.h
@@ -102,6 +102,7 @@ static const unsigned int DATABASE_WRITE_INTERVAL = 60 * 60;
|
||||
static const unsigned int DATABASE_FLUSH_INTERVAL = 24 * 60 * 60;
|
||||
/** Maximum length of reject messages. */
|
||||
static const unsigned int MAX_REJECT_MESSAGE_LENGTH = 111;
|
||||
static const int64_t DEFAULT_MAX_TIP_AGE = 24 * 60 * 60;
|
||||
|
||||
//static const bool DEFAULT_ADDRESSINDEX = false;
|
||||
//static const bool DEFAULT_SPENTINDEX = false;
|
||||
@@ -149,6 +150,7 @@ extern bool fCoinbaseEnforcedProtectionEnabled;
|
||||
extern size_t nCoinCacheUsage;
|
||||
extern CFeeRate minRelayTxFee;
|
||||
extern bool fAlerts;
|
||||
extern int64_t nMaxTipAge;
|
||||
|
||||
/** Best header we've seen so far (used for getheaders queries' starting points). */
|
||||
extern CBlockIndex *pindexBestHeader;
|
||||
@@ -287,11 +289,11 @@ struct CTimestampIndexIteratorKey {
|
||||
return 4;
|
||||
}
|
||||
template<typename Stream>
|
||||
void Serialize(Stream& s, int nType, int nVersion) const {
|
||||
void Serialize(Stream& s) const {
|
||||
ser_writedata32be(s, timestamp);
|
||||
}
|
||||
template<typename Stream>
|
||||
void Unserialize(Stream& s, int nType, int nVersion) {
|
||||
void Unserialize(Stream& s) {
|
||||
timestamp = ser_readdata32be(s);
|
||||
}
|
||||
|
||||
@@ -316,14 +318,14 @@ struct CTimestampIndexKey {
|
||||
return 36;
|
||||
}
|
||||
template<typename Stream>
|
||||
void Serialize(Stream& s, int nType, int nVersion) const {
|
||||
void Serialize(Stream& s) const {
|
||||
ser_writedata32be(s, timestamp);
|
||||
blockHash.Serialize(s, nType, nVersion);
|
||||
blockHash.Serialize(s);
|
||||
}
|
||||
template<typename Stream>
|
||||
void Unserialize(Stream& s, int nType, int nVersion) {
|
||||
void Unserialize(Stream& s) {
|
||||
timestamp = ser_readdata32be(s);
|
||||
blockHash.Unserialize(s, nType, nVersion);
|
||||
blockHash.Unserialize(s);
|
||||
}
|
||||
|
||||
CTimestampIndexKey(unsigned int time, uint256 hash) {
|
||||
@@ -349,13 +351,13 @@ struct CTimestampBlockIndexKey {
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
void Serialize(Stream& s, int nType, int nVersion) const {
|
||||
blockHash.Serialize(s, nType, nVersion);
|
||||
void Serialize(Stream& s) const {
|
||||
blockHash.Serialize(s);
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
void Unserialize(Stream& s, int nType, int nVersion) {
|
||||
blockHash.Unserialize(s, nType, nVersion);
|
||||
void Unserialize(Stream& s) {
|
||||
blockHash.Unserialize(s);
|
||||
}
|
||||
|
||||
CTimestampBlockIndexKey(uint256 hash) {
|
||||
@@ -378,12 +380,12 @@ struct CTimestampBlockIndexValue {
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
void Serialize(Stream& s, int nType, int nVersion) const {
|
||||
void Serialize(Stream& s) const {
|
||||
ser_writedata32be(s, ltimestamp);
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
void Unserialize(Stream& s, int nType, int nVersion) {
|
||||
void Unserialize(Stream& s) {
|
||||
ltimestamp = ser_readdata32be(s);
|
||||
}
|
||||
|
||||
@@ -410,17 +412,17 @@ struct CAddressUnspentKey {
|
||||
return 57;
|
||||
}
|
||||
template<typename Stream>
|
||||
void Serialize(Stream& s, int nType, int nVersion) const {
|
||||
void Serialize(Stream& s) const {
|
||||
ser_writedata8(s, type);
|
||||
hashBytes.Serialize(s, nType, nVersion);
|
||||
txhash.Serialize(s, nType, nVersion);
|
||||
hashBytes.Serialize(s);
|
||||
txhash.Serialize(s);
|
||||
ser_writedata32(s, index);
|
||||
}
|
||||
template<typename Stream>
|
||||
void Unserialize(Stream& s, int nType, int nVersion) {
|
||||
void Unserialize(Stream& s) {
|
||||
type = ser_readdata8(s);
|
||||
hashBytes.Unserialize(s, nType, nVersion);
|
||||
txhash.Unserialize(s, nType, nVersion);
|
||||
hashBytes.Unserialize(s);
|
||||
txhash.Unserialize(s);
|
||||
index = ser_readdata32(s);
|
||||
}
|
||||
|
||||
@@ -443,7 +445,7 @@ struct CAddressUnspentKey {
|
||||
}
|
||||
};
|
||||
|
||||
struct CAddressUnspentValue {
|
||||
struct CAddressUnspentValue {
|
||||
CAmount satoshis;
|
||||
CScript script;
|
||||
int blockHeight;
|
||||
@@ -451,9 +453,9 @@ struct CAddressUnspentValue {
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(satoshis);
|
||||
READWRITE(script);
|
||||
READWRITE(*(CScriptBase*)(&script));
|
||||
READWRITE(blockHeight);
|
||||
}
|
||||
|
||||
@@ -491,24 +493,24 @@ struct CAddressIndexKey {
|
||||
return 66;
|
||||
}
|
||||
template<typename Stream>
|
||||
void Serialize(Stream& s, int nType, int nVersion) const {
|
||||
void Serialize(Stream& s) const {
|
||||
ser_writedata8(s, type);
|
||||
hashBytes.Serialize(s, nType, nVersion);
|
||||
hashBytes.Serialize(s);
|
||||
// Heights are stored big-endian for key sorting in LevelDB
|
||||
ser_writedata32be(s, blockHeight);
|
||||
ser_writedata32be(s, txindex);
|
||||
txhash.Serialize(s, nType, nVersion);
|
||||
txhash.Serialize(s);
|
||||
ser_writedata32(s, index);
|
||||
char f = spending;
|
||||
ser_writedata8(s, f);
|
||||
}
|
||||
template<typename Stream>
|
||||
void Unserialize(Stream& s, int nType, int nVersion) {
|
||||
void Unserialize(Stream& s) {
|
||||
type = ser_readdata8(s);
|
||||
hashBytes.Unserialize(s, nType, nVersion);
|
||||
hashBytes.Unserialize(s);
|
||||
blockHeight = ser_readdata32be(s);
|
||||
txindex = ser_readdata32be(s);
|
||||
txhash.Unserialize(s, nType, nVersion);
|
||||
txhash.Unserialize(s);
|
||||
index = ser_readdata32(s);
|
||||
char f = ser_readdata8(s);
|
||||
spending = f;
|
||||
@@ -549,14 +551,14 @@ struct CAddressIndexIteratorKey {
|
||||
return 21;
|
||||
}
|
||||
template<typename Stream>
|
||||
void Serialize(Stream& s, int nType, int nVersion) const {
|
||||
void Serialize(Stream& s) const {
|
||||
ser_writedata8(s, type);
|
||||
hashBytes.Serialize(s, nType, nVersion);
|
||||
hashBytes.Serialize(s);
|
||||
}
|
||||
template<typename Stream>
|
||||
void Unserialize(Stream& s, int nType, int nVersion) {
|
||||
void Unserialize(Stream& s) {
|
||||
type = ser_readdata8(s);
|
||||
hashBytes.Unserialize(s, nType, nVersion);
|
||||
hashBytes.Unserialize(s);
|
||||
}
|
||||
|
||||
CAddressIndexIteratorKey(unsigned int addressType, uint160 addressHash) {
|
||||
@@ -583,15 +585,15 @@ struct CAddressIndexIteratorHeightKey {
|
||||
return 25;
|
||||
}
|
||||
template<typename Stream>
|
||||
void Serialize(Stream& s, int nType, int nVersion) const {
|
||||
void Serialize(Stream& s) const {
|
||||
ser_writedata8(s, type);
|
||||
hashBytes.Serialize(s, nType, nVersion);
|
||||
hashBytes.Serialize(s);
|
||||
ser_writedata32be(s, blockHeight);
|
||||
}
|
||||
template<typename Stream>
|
||||
void Unserialize(Stream& s, int nType, int nVersion) {
|
||||
void Unserialize(Stream& s) {
|
||||
type = ser_readdata8(s);
|
||||
hashBytes.Unserialize(s, nType, nVersion);
|
||||
hashBytes.Unserialize(s);
|
||||
blockHeight = ser_readdata32be(s);
|
||||
}
|
||||
|
||||
@@ -619,7 +621,7 @@ struct CDiskTxPos : public CDiskBlockPos
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(*(CDiskBlockPos*)this);
|
||||
READWRITE(VARINT(nTxOffset));
|
||||
}
|
||||
@@ -829,8 +831,11 @@ bool AcceptBlockHeader(int32_t *futureblockp,const CBlockHeader& block, CValidat
|
||||
* When there are blocks in the active chain with missing data (e.g. if the
|
||||
* activation height and branch ID of a particular upgrade have been altered),
|
||||
* rewind the chainstate and remove them from the block index.
|
||||
*
|
||||
* clearWitnessCaches is an output parameter that will be set to true iff
|
||||
* witness caches should be cleared in order to handle an intended long rewind.
|
||||
*/
|
||||
bool RewindBlockIndex(const CChainParams& params);
|
||||
bool RewindBlockIndex(const CChainParams& params, bool& clearWitnessCaches);
|
||||
|
||||
class CBlockFileInfo
|
||||
{
|
||||
@@ -846,7 +851,7 @@ public:
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(VARINT(nBlocks));
|
||||
READWRITE(VARINT(nSize));
|
||||
READWRITE(VARINT(nUndoSize));
|
||||
|
||||
@@ -46,7 +46,9 @@ template<typename X> static inline size_t DynamicUsage(const X * const &v) { ret
|
||||
static inline size_t MallocUsage(size_t alloc)
|
||||
{
|
||||
// Measured on libc6 2.19 on Linux.
|
||||
if (sizeof(void*) == 8) {
|
||||
if (alloc == 0) {
|
||||
return 0;
|
||||
} else if (sizeof(void*) == 8) {
|
||||
return ((alloc + 31) >> 4) << 4;
|
||||
} else if (sizeof(void*) == 4) {
|
||||
return ((alloc + 15) >> 3) << 3;
|
||||
@@ -74,6 +76,12 @@ static inline size_t DynamicUsage(const std::vector<X>& v)
|
||||
return MallocUsage(v.capacity() * sizeof(X));
|
||||
}
|
||||
|
||||
template<unsigned int N, typename X, typename S, typename D>
|
||||
static inline size_t DynamicUsage(const prevector<N, X, S, D>& v)
|
||||
{
|
||||
return MallocUsage(v.allocated_memory());
|
||||
}
|
||||
|
||||
template<typename X>
|
||||
static inline size_t DynamicUsage(const std::set<X>& s)
|
||||
{
|
||||
|
||||
@@ -85,7 +85,7 @@ public:
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(nTransactions);
|
||||
READWRITE(vHash);
|
||||
std::vector<unsigned char> vBytes;
|
||||
@@ -147,7 +147,7 @@ public:
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(header);
|
||||
READWRITE(txn);
|
||||
}
|
||||
|
||||
@@ -73,6 +73,14 @@ double AtomicTimer::rate(const AtomicCounter& count)
|
||||
return duration > 0 ? (double)count.get() / duration : 0;
|
||||
}
|
||||
|
||||
boost::synchronized_value<int64_t> nNodeStartTime;
|
||||
boost::synchronized_value<int64_t> nNextRefresh;
|
||||
int64_t nHashCount;
|
||||
AtomicCounter transactionsValidated;
|
||||
AtomicCounter ehSolverRuns;
|
||||
AtomicCounter solutionTargetChecks;
|
||||
static AtomicCounter minedBlocks;
|
||||
AtomicTimer miningTimer;
|
||||
CCriticalSection cs_metrics;
|
||||
|
||||
double AtomicTimer::rate(const int64_t count)
|
||||
@@ -87,20 +95,11 @@ double AtomicTimer::rate(const int64_t count)
|
||||
return duration > 0 ? (double)count / duration : 0;
|
||||
}
|
||||
|
||||
boost::synchronized_value<int64_t> nNodeStartTime;
|
||||
boost::synchronized_value<int64_t> nNextRefresh;
|
||||
int64_t nHashCount;
|
||||
AtomicCounter transactionsValidated;
|
||||
AtomicCounter ehSolverRuns;
|
||||
AtomicCounter solutionTargetChecks;
|
||||
AtomicCounter minedBlocks;
|
||||
AtomicTimer miningTimer;
|
||||
static boost::synchronized_value<std::list<uint256>> trackedBlocks;
|
||||
|
||||
boost::synchronized_value<std::list<uint256>> trackedBlocks;
|
||||
|
||||
boost::synchronized_value<std::list<std::string>> messageBox;
|
||||
boost::synchronized_value<std::string> initMessage;
|
||||
bool loaded = false;
|
||||
static boost::synchronized_value<std::list<std::string>> messageBox;
|
||||
static boost::synchronized_value<std::string> initMessage;
|
||||
static bool loaded = false;
|
||||
|
||||
extern int64_t GetNetworkHashPS(int lookup, int height);
|
||||
|
||||
@@ -439,6 +438,30 @@ int printInitMessage()
|
||||
return 2;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
|
||||
|
||||
bool enableVTMode()
|
||||
{
|
||||
// Set output mode to handle virtual terminal sequences
|
||||
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (hOut == INVALID_HANDLE_VALUE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD dwMode = 0;
|
||||
if (!GetConsoleMode(hOut, &dwMode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
||||
if (!SetConsoleMode(hOut, dwMode)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void ThreadShowMetricsScreen()
|
||||
{
|
||||
// Make this thread recognisable as the metrics screen thread
|
||||
@@ -450,6 +473,10 @@ void ThreadShowMetricsScreen()
|
||||
int64_t nRefresh = GetArg("-metricsrefreshtime", isTTY ? 1 : 600);
|
||||
|
||||
if (isScreen) {
|
||||
#ifdef WIN32
|
||||
enableVTMode();
|
||||
#endif
|
||||
|
||||
// Clear screen
|
||||
std::cout << "\e[2J";
|
||||
|
||||
@@ -474,15 +501,16 @@ void ThreadShowMetricsScreen()
|
||||
// Get current window size
|
||||
if (isTTY) {
|
||||
#ifdef _WIN32
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
|
||||
cols = csbi.srWindow.Right - csbi.srWindow.Left + 1;
|
||||
#else
|
||||
struct winsize w;
|
||||
w.ws_col = 0;
|
||||
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1 && w.ws_col != 0) {
|
||||
cols = w.ws_col;
|
||||
}
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi) != 0) {
|
||||
cols = csbi.srWindow.Right - csbi.srWindow.Left + 1;
|
||||
}
|
||||
#else
|
||||
struct winsize w;
|
||||
w.ws_col = 0;
|
||||
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1 && w.ws_col != 0) {
|
||||
cols = w.ws_col;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -508,7 +536,13 @@ void ThreadShowMetricsScreen()
|
||||
|
||||
if (isScreen) {
|
||||
// Explain how to exit
|
||||
std::cout << "[" << _("Press Ctrl+C to exit") << "] [" << _("Set 'showmetrics=0' to hide") << "]" << std::endl;
|
||||
std::cout << "[";
|
||||
#ifdef WIN32
|
||||
std::cout << _("'zcash-cli.exe stop' to exit");
|
||||
#else
|
||||
std::cout << _("Press Ctrl+C to exit");
|
||||
#endif
|
||||
std::cout << "] [" << _("Set 'showmetrics=0' to hide") << "]" << std::endl;
|
||||
} else {
|
||||
// Print delineator
|
||||
std::cout << "----------------------------------------" << std::endl;
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#endif
|
||||
|
||||
#include "amount.h"
|
||||
#include "base58.h"
|
||||
#include "chainparams.h"
|
||||
#include "importcoin.h"
|
||||
#include "consensus/consensus.h"
|
||||
@@ -20,7 +19,7 @@
|
||||
#include "crypto/verus_hash.h"
|
||||
#endif
|
||||
#include "hash.h"
|
||||
|
||||
#include "key_io.h"
|
||||
#include "main.h"
|
||||
#include "metrics.h"
|
||||
#include "net.h"
|
||||
@@ -198,6 +197,9 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
|
||||
CCoinsViewCache view(pcoinsTip);
|
||||
uint32_t expired; uint64_t commission;
|
||||
|
||||
SaplingMerkleTree sapling_tree;
|
||||
assert(view.GetSaplingAnchorAt(view.GetBestAnchor(SAPLING), sapling_tree));
|
||||
|
||||
// Priority order to process transactions
|
||||
list<COrphan> vOrphan; // list memory doesn't move
|
||||
map<uint256, vector<COrphan*> > mapDependers;
|
||||
@@ -275,7 +277,7 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
|
||||
|
||||
dPriority += (double)nValueIn * nConf;
|
||||
}
|
||||
nTotalIn += tx.GetJoinSplitValueIn();
|
||||
nTotalIn += tx.GetShieldedValueIn();
|
||||
}
|
||||
|
||||
if (fMissingInputs) continue;
|
||||
@@ -377,7 +379,11 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
|
||||
continue;
|
||||
}
|
||||
UpdateCoins(tx, view, nHeight);
|
||||
|
||||
|
||||
BOOST_FOREACH(const OutputDescription &outDescription, tx.vShieldedOutput) {
|
||||
sapling_tree.append(outDescription.cm);
|
||||
}
|
||||
|
||||
// Added
|
||||
pblock->vtx.push_back(tx);
|
||||
pblocktemplate->vTxFees.push_back(nTxFees);
|
||||
@@ -495,7 +501,7 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
|
||||
txNew.vout.resize(2);
|
||||
txNew.vout[1].nValue = commission;
|
||||
txNew.vout[1].scriptPubKey.resize(35);
|
||||
ptr = (uint8_t *)txNew.vout[1].scriptPubKey.data();
|
||||
ptr = (uint8_t *)&txNew.vout[1].scriptPubKey[0];
|
||||
ptr[0] = 33;
|
||||
for (i=0; i<33; i++)
|
||||
ptr[i+1] = ASSETCHAINS_OVERRIDE_PUBKEY33[i];
|
||||
@@ -520,7 +526,7 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
|
||||
|
||||
// Fill in header
|
||||
pblock->hashPrevBlock = pindexPrev->GetBlockHash();
|
||||
pblock->hashReserved = uint256();
|
||||
pblock->hashFinalSaplingRoot = sapling_tree.root();
|
||||
|
||||
// all Verus PoS chains need this data in the block at all times
|
||||
if ( ASSETCHAINS_LWMAPOS || ASSETCHAINS_SYMBOL[0] == 0 || ASSETCHAINS_STAKED == 0 || KOMODO_MININGTHREADS > 0 )
|
||||
@@ -569,32 +575,12 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
|
||||
//fprintf(stderr,"check validity\n");
|
||||
if ( !TestBlockValidity(state, *pblock, pindexPrev, false, false)) // invokes CC checks
|
||||
{
|
||||
//static uint32_t counter;
|
||||
//if ( counter++ < 100 && ASSETCHAINS_STAKED == 0 )
|
||||
// fprintf(stderr,"warning: miner testblockvalidity failed\n");
|
||||
fprintf(stderr,"invalid\n");
|
||||
return(0);
|
||||
throw std::runtime_error("CreateNewBlock(): TestBlockValidity failed");
|
||||
}
|
||||
//fprintf(stderr,"valid\n");
|
||||
}
|
||||
}
|
||||
/* skip checking validity outside of lock. if inside lock and CC contract is being validated, can deadlock.
|
||||
if ( ASSETCHAINS_CC != 0 && pindexPrev != 0 && ASSETCHAINS_STAKED == 0 && (ASSETCHAINS_SYMBOL[0] != 0 || IS_KOMODO_NOTARY == 0 || My_notaryid < 0) )
|
||||
{
|
||||
CValidationState state;
|
||||
//fprintf(stderr,"check validity\n");
|
||||
if ( !TestBlockValidity(state, *pblock, pindexPrev, false, false)) // invokes CC checks
|
||||
{
|
||||
//static uint32_t counter;
|
||||
//if ( counter++ < 100 && ASSETCHAINS_STAKED == 0 )
|
||||
// fprintf(stderr,"warning: miner testblockvalidity failed\n");
|
||||
fprintf(stderr,"invalid\n");
|
||||
return(0);
|
||||
}
|
||||
//fprintf(stderr,"valid\n");
|
||||
}*/
|
||||
//fprintf(stderr,"done new block\n");
|
||||
|
||||
return pblocktemplate.release();
|
||||
}
|
||||
|
||||
@@ -675,7 +661,7 @@ void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int&
|
||||
|
||||
CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey, int32_t nHeight, int32_t gpucount, bool isStake)
|
||||
{
|
||||
CPubKey pubkey; CScript scriptPubKey; uint8_t *script,*ptr; int32_t i;
|
||||
CPubKey pubkey; CScript scriptPubKey; uint8_t *ptr; int32_t i;
|
||||
if ( nHeight == 1 && ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 )
|
||||
{
|
||||
scriptPubKey = CScript() << ParseHex(ASSETCHAINS_OVERRIDE_PUBKEY) << OP_CHECKSIG;
|
||||
@@ -693,11 +679,10 @@ CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey, int32_t nHeight,
|
||||
}
|
||||
scriptPubKey.resize(35);
|
||||
ptr = (uint8_t *)pubkey.begin();
|
||||
script = (uint8_t *)scriptPubKey.data();
|
||||
script[0] = 33;
|
||||
scriptPubKey[0] = 33;
|
||||
for (i=0; i<33; i++)
|
||||
script[i+1] = ptr[i];
|
||||
script[34] = OP_CHECKSIG;
|
||||
scriptPubKey[i+1] = ptr[i];
|
||||
scriptPubKey[34] = OP_CHECKSIG;
|
||||
//scriptPubKey = CScript() << ToByteVector(pubkey) << OP_CHECKSIG;
|
||||
}
|
||||
return CreateNewBlock(scriptPubKey, gpucount, isStake);
|
||||
|
||||
17
src/net.cpp
17
src/net.cpp
@@ -76,6 +76,7 @@ static std::vector<ListenSocket> vhListenSocket;
|
||||
CAddrMan addrman;
|
||||
int nMaxConnections = DEFAULT_MAX_PEER_CONNECTIONS;
|
||||
bool fAddressesInitialized = false;
|
||||
std::string strSubVersion;
|
||||
|
||||
vector<CNode*> vNodes;
|
||||
CCriticalSection cs_vNodes;
|
||||
@@ -85,10 +86,10 @@ CCriticalSection cs_mapRelay;
|
||||
limitedmap<CInv, int64_t> mapAlreadyAskedFor(MAX_INV_SZ);
|
||||
|
||||
static deque<string> vOneShots;
|
||||
CCriticalSection cs_vOneShots;
|
||||
static CCriticalSection cs_vOneShots;
|
||||
|
||||
set<CNetAddr> setservAddNodeAddresses;
|
||||
CCriticalSection cs_setservAddNodeAddresses;
|
||||
static set<CNetAddr> setservAddNodeAddresses;
|
||||
static CCriticalSection cs_setservAddNodeAddresses;
|
||||
|
||||
vector<std::string> vAddedNodes;
|
||||
CCriticalSection cs_vAddedNodes;
|
||||
@@ -97,7 +98,7 @@ NodeId nLastNodeId = 0;
|
||||
CCriticalSection cs_nLastNodeId;
|
||||
|
||||
static CSemaphore *semOutbound = NULL;
|
||||
boost::condition_variable messageHandlerCondition;
|
||||
static boost::condition_variable messageHandlerCondition;
|
||||
|
||||
// Signals for message handling
|
||||
static CNodeSignals g_signals;
|
||||
@@ -439,7 +440,7 @@ void CNode::PushVersion()
|
||||
else
|
||||
LogPrint("net", "send version message: version %d, blocks=%d, us=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), id);
|
||||
PushMessage("version", PROTOCOL_VERSION, nLocalServices, nTime, addrYou, addrMe,
|
||||
nLocalHostNonce, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector<string>()), nBestHeight, true);
|
||||
nLocalHostNonce, strSubVersion, nBestHeight, true);
|
||||
}
|
||||
|
||||
|
||||
@@ -823,7 +824,7 @@ static bool AttemptToEvictConnection(bool fPreferNewConnection) {
|
||||
}
|
||||
|
||||
const Consensus::Params& params = Params().GetConsensus();
|
||||
int nActivationHeight = params.vUpgrades[Consensus::UPGRADE_OVERWINTER].nActivationHeight;
|
||||
int nActivationHeight = params.vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight;
|
||||
|
||||
if (nActivationHeight > 0 &&
|
||||
height < nActivationHeight &&
|
||||
@@ -831,7 +832,7 @@ static bool AttemptToEvictConnection(bool fPreferNewConnection) {
|
||||
{
|
||||
// Find any nodes which don't support Overwinter protocol version
|
||||
BOOST_FOREACH(const CNodeRef &node, vEvictionCandidates) {
|
||||
if (node->nVersion < params.vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion) {
|
||||
if (node->nVersion < params.vUpgrades[Consensus::UPGRADE_SAPLING].nProtocolVersion) {
|
||||
vTmpEvictionCandidates.push_back(node);
|
||||
}
|
||||
}
|
||||
@@ -1819,7 +1820,7 @@ bool StopNode()
|
||||
return true;
|
||||
}
|
||||
|
||||
class CNetCleanup
|
||||
static class CNetCleanup
|
||||
{
|
||||
public:
|
||||
CNetCleanup() {}
|
||||
|
||||
@@ -49,6 +49,8 @@ static const unsigned int MAX_INV_SZ = 50000;
|
||||
static const unsigned int MAX_ADDR_TO_SEND = 1000;
|
||||
/** Maximum length of incoming protocol messages (no message over 2 MiB is currently acceptable). */
|
||||
static const unsigned int MAX_PROTOCOL_MESSAGE_LENGTH = 2 * 1024 * 1024;
|
||||
/** Maximum length of strSubVer in `version` message */
|
||||
static const unsigned int MAX_SUBVERSION_LENGTH = 256;
|
||||
/** -listen default */
|
||||
static const bool DEFAULT_LISTEN = true;
|
||||
/** The maximum number of entries in mapAskFor */
|
||||
@@ -156,6 +158,9 @@ extern CCriticalSection cs_vAddedNodes;
|
||||
extern NodeId nLastNodeId;
|
||||
extern CCriticalSection cs_nLastNodeId;
|
||||
|
||||
/** Subversion as sent to the P2P network in `version` messages */
|
||||
extern std::string strSubVersion;
|
||||
|
||||
struct LocalServiceInfo {
|
||||
int nScore;
|
||||
int nPort;
|
||||
|
||||
@@ -97,7 +97,7 @@ class CNetAddr
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(FLATDATA(ip));
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@ class CService : public CNetAddr
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(FLATDATA(ip));
|
||||
unsigned short portN = htons(port);
|
||||
READWRITE(FLATDATA(portN));
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "leveldbwrapper.h"
|
||||
#include "dbwrapper.h"
|
||||
#include "notarisationdb.h"
|
||||
#include "uint256.h"
|
||||
#include "cc/eval.h"
|
||||
@@ -10,7 +10,7 @@
|
||||
NotarisationDB *pnotarisations;
|
||||
|
||||
|
||||
NotarisationDB::NotarisationDB(size_t nCacheSize, bool fMemory, bool fWipe) : CLevelDBWrapper(GetDataDir() / "notarisations", nCacheSize, fMemory, fWipe, false, 64) { }
|
||||
NotarisationDB::NotarisationDB(size_t nCacheSize, bool fMemory, bool fWipe) : CDBWrapper(GetDataDir() / "notarisations", nCacheSize, fMemory, fWipe, false, 64) { }
|
||||
|
||||
|
||||
NotarisationsInBlock ScanBlockNotarisations(const CBlock &block, int nHeight)
|
||||
@@ -67,7 +67,7 @@ bool GetBackNotarisation(uint256 notarisationHash, Notarisation &n)
|
||||
/*
|
||||
* Write an index of KMD notarisation id -> backnotarisation
|
||||
*/
|
||||
void WriteBackNotarisations(const NotarisationsInBlock notarisations, CLevelDBBatch &batch)
|
||||
void WriteBackNotarisations(const NotarisationsInBlock notarisations, CDBBatch &batch)
|
||||
{
|
||||
int wrote = 0;
|
||||
BOOST_FOREACH(const Notarisation &n, notarisations)
|
||||
@@ -80,7 +80,7 @@ void WriteBackNotarisations(const NotarisationsInBlock notarisations, CLevelDBBa
|
||||
}
|
||||
|
||||
|
||||
void EraseBackNotarisations(const NotarisationsInBlock notarisations, CLevelDBBatch &batch)
|
||||
void EraseBackNotarisations(const NotarisationsInBlock notarisations, CDBBatch &batch)
|
||||
{
|
||||
BOOST_FOREACH(const Notarisation &n, notarisations)
|
||||
{
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
#define NOTARISATIONDB_H
|
||||
|
||||
#include "uint256.h"
|
||||
#include "leveldbwrapper.h"
|
||||
#include "dbwrapper.h"
|
||||
#include "cc/eval.h"
|
||||
|
||||
|
||||
class NotarisationDB : public CLevelDBWrapper
|
||||
class NotarisationDB : public CDBWrapper
|
||||
{
|
||||
public:
|
||||
NotarisationDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false);
|
||||
@@ -21,8 +21,8 @@ typedef std::vector<Notarisation> NotarisationsInBlock;
|
||||
NotarisationsInBlock ScanBlockNotarisations(const CBlock &block, int nHeight);
|
||||
bool GetBlockNotarisations(uint256 blockHash, NotarisationsInBlock &nibs);
|
||||
bool GetBackNotarisation(uint256 notarisationHash, Notarisation &n);
|
||||
void WriteBackNotarisations(const NotarisationsInBlock notarisations, CLevelDBBatch &batch);
|
||||
void EraseBackNotarisations(const NotarisationsInBlock notarisations, CLevelDBBatch &batch);
|
||||
void WriteBackNotarisations(const NotarisationsInBlock notarisations, CDBBatch &batch);
|
||||
void EraseBackNotarisations(const NotarisationsInBlock notarisations, CDBBatch &batch);
|
||||
int ScanNotarisationsDB(int height, std::string symbol, int scanLimitBlocks, Notarisation& out);
|
||||
bool IsTXSCL(const char* symbol);
|
||||
|
||||
|
||||
@@ -3,11 +3,13 @@
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "paymentdisclosure.h"
|
||||
|
||||
#include "key_io.h"
|
||||
#include "util.h"
|
||||
|
||||
std::string PaymentDisclosureInfo::ToString() const {
|
||||
return strprintf("PaymentDisclosureInfo(version=%d, esk=%s, joinSplitPrivKey=<omitted>, address=%s)",
|
||||
version, esk.ToString(), CZCPaymentAddress(zaddr).ToString());
|
||||
version, esk.ToString(), EncodePaymentAddress(zaddr));
|
||||
}
|
||||
|
||||
std::string PaymentDisclosure::ToString() const {
|
||||
@@ -17,7 +19,7 @@ std::string PaymentDisclosure::ToString() const {
|
||||
|
||||
std::string PaymentDisclosurePayload::ToString() const {
|
||||
return strprintf("PaymentDisclosurePayload(version=%d, esk=%s, txid=%s, js=%d, n=%d, address=%s, message=%s)",
|
||||
version, esk.ToString(), txid.ToString(), js, n, CZCPaymentAddress(zaddr).ToString(), message);
|
||||
version, esk.ToString(), txid.ToString(), js, n, EncodePaymentAddress(zaddr), message);
|
||||
}
|
||||
|
||||
PaymentDisclosure::PaymentDisclosure(const uint256 &joinSplitPubKey, const PaymentDisclosureKey &key, const PaymentDisclosureInfo &info, const std::string &message)
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
// For JSOutPoint
|
||||
#include "wallet/wallet.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
@@ -38,17 +39,17 @@ struct PaymentDisclosureInfo {
|
||||
uint256 joinSplitPrivKey; // primitives/transaction.h
|
||||
// ed25519 - not tied to implementation e.g. libsodium, see ed25519 rfc
|
||||
|
||||
libzcash::PaymentAddress zaddr;
|
||||
libzcash::SproutPaymentAddress zaddr;
|
||||
|
||||
PaymentDisclosureInfo() : version(PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL) {
|
||||
}
|
||||
|
||||
PaymentDisclosureInfo(uint8_t v, uint256 esk, uint256 key, libzcash::PaymentAddress zaddr) : version(v), esk(esk), joinSplitPrivKey(key), zaddr(zaddr) { }
|
||||
PaymentDisclosureInfo(uint8_t v, uint256 esk, uint256 key, libzcash::SproutPaymentAddress zaddr) : version(v), esk(esk), joinSplitPrivKey(key), zaddr(zaddr) { }
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(version);
|
||||
READWRITE(esk);
|
||||
READWRITE(joinSplitPrivKey);
|
||||
@@ -73,19 +74,15 @@ struct PaymentDisclosurePayload {
|
||||
uint8_t version; // 0 = experimental, 1 = first production version, etc.
|
||||
uint256 esk; // zcash/NoteEncryption.cpp
|
||||
uint256 txid; // primitives/transaction.h
|
||||
#ifdef __LP64__
|
||||
uint64_t js;
|
||||
#else
|
||||
size_t js; // Index into CTransaction.vjoinsplit
|
||||
#endif
|
||||
uint64_t js; // Index into CTransaction.vjoinsplit
|
||||
uint8_t n; // Index into JSDescription fields of length ZC_NUM_JS_OUTPUTS
|
||||
libzcash::PaymentAddress zaddr; // zcash/Address.hpp
|
||||
libzcash::SproutPaymentAddress zaddr; // zcash/Address.hpp
|
||||
std::string message; // parameter to RPC call
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(marker);
|
||||
READWRITE(version);
|
||||
READWRITE(esk);
|
||||
@@ -116,18 +113,18 @@ struct PaymentDisclosurePayload {
|
||||
};
|
||||
|
||||
struct PaymentDisclosure {
|
||||
PaymentDisclosurePayload payload;
|
||||
boost::array<unsigned char, 64> payloadSig;
|
||||
PaymentDisclosurePayload payload;
|
||||
std::array<unsigned char, 64> payloadSig;
|
||||
// We use boost array because serialize doesn't like char buffer, otherwise we could do: unsigned char payloadSig[64];
|
||||
|
||||
PaymentDisclosure() {};
|
||||
PaymentDisclosure(const PaymentDisclosurePayload payload, const boost::array<unsigned char, 64> sig) : payload(payload), payloadSig(sig) {};
|
||||
PaymentDisclosure(const PaymentDisclosurePayload payload, const std::array<unsigned char, 64> sig) : payload(payload), payloadSig(sig) {};
|
||||
PaymentDisclosure(const uint256& joinSplitPubKey, const PaymentDisclosureKey& key, const PaymentDisclosureInfo& info, const std::string& message);
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(payload);
|
||||
READWRITE(payloadSig);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "paymentdisclosuredb.h"
|
||||
|
||||
#include "util.h"
|
||||
#include "leveldbwrapper.h"
|
||||
#include "dbwrapper.h"
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
@@ -38,7 +38,7 @@ PaymentDisclosureDB::PaymentDisclosureDB(const boost::filesystem::path& dbPath)
|
||||
TryCreateDirectory(path);
|
||||
options.create_if_missing = true;
|
||||
leveldb::Status status = leveldb::DB::Open(options, path.string(), &db);
|
||||
HandleError(status); // throws exception
|
||||
dbwrapper_private::HandleError(status); // throws exception
|
||||
LogPrintf("PaymentDisclosure: Opened LevelDB successfully\n");
|
||||
}
|
||||
|
||||
@@ -57,12 +57,12 @@ bool PaymentDisclosureDB::Put(const PaymentDisclosureKey& key, const PaymentDisc
|
||||
std::lock_guard<std::mutex> guard(lock_);
|
||||
|
||||
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
|
||||
ssValue.reserve(ssValue.GetSerializeSize(info));
|
||||
ssValue.reserve(GetSerializeSize(ssValue, info));
|
||||
ssValue << info;
|
||||
leveldb::Slice slice(&ssValue[0], ssValue.size());
|
||||
|
||||
leveldb::Status status = db->Put(writeOptions, key.ToString(), slice);
|
||||
HandleError(status);
|
||||
dbwrapper_private::HandleError(status);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ bool PaymentDisclosureDB::Get(const PaymentDisclosureKey& key, PaymentDisclosure
|
||||
if (status.IsNotFound())
|
||||
return false;
|
||||
LogPrintf("PaymentDisclosure: LevelDB read failure: %s\n", status.ToString());
|
||||
HandleError(status);
|
||||
dbwrapper_private::HandleError(status);
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@@ -322,14 +322,6 @@ bool CheckEquihashSolution(const CBlockHeader *pblock, const CChainParams& param
|
||||
// H(I||V||...
|
||||
crypto_generichash_blake2b_update(&state, (unsigned char*)&ss[0], ss.size());
|
||||
|
||||
#ifdef ENABLE_RUST
|
||||
// Ensure that our Rust interactions are working in production builds. This is
|
||||
// temporary and should be removed.
|
||||
{
|
||||
assert(librustzcash_xor(0x0f0f0f0f0f0f0f0f, 0x1111111111111111) == 0x1e1e1e1e1e1e1e1e);
|
||||
}
|
||||
#endif // ENABLE_RUST
|
||||
|
||||
bool isValid;
|
||||
EhIsValidSolution(n, k, state, pblock->nSolution, isValid);
|
||||
|
||||
|
||||
494
src/prevector.h
Normal file
494
src/prevector.h
Normal file
@@ -0,0 +1,494 @@
|
||||
#ifndef _BITCOIN_PREVECTOR_H_
|
||||
#define _BITCOIN_PREVECTOR_H_
|
||||
|
||||
#include <util.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#pragma pack(push, 1)
|
||||
/** Implements a drop-in replacement for std::vector<T> which stores up to N
|
||||
* elements directly (without heap allocation). The types Size and Diff are
|
||||
* used to store element counts, and can be any unsigned + signed type.
|
||||
*
|
||||
* Storage layout is either:
|
||||
* - Direct allocation:
|
||||
* - Size _size: the number of used elements (between 0 and N)
|
||||
* - T direct[N]: an array of N elements of type T
|
||||
* (only the first _size are initialized).
|
||||
* - Indirect allocation:
|
||||
* - Size _size: the number of used elements plus N + 1
|
||||
* - Size capacity: the number of allocated elements
|
||||
* - T* indirect: a pointer to an array of capacity elements of type T
|
||||
* (only the first _size are initialized).
|
||||
*
|
||||
* The data type T must be movable by memmove/realloc(). Once we switch to C++,
|
||||
* move constructors can be used instead.
|
||||
*/
|
||||
template<unsigned int N, typename T, typename Size = uint32_t, typename Diff = int32_t>
|
||||
class prevector {
|
||||
public:
|
||||
typedef Size size_type;
|
||||
typedef Diff difference_type;
|
||||
typedef T value_type;
|
||||
typedef value_type& reference;
|
||||
typedef const value_type& const_reference;
|
||||
typedef value_type* pointer;
|
||||
typedef const value_type* const_pointer;
|
||||
|
||||
class iterator {
|
||||
T* ptr;
|
||||
public:
|
||||
typedef Diff difference_type;
|
||||
typedef T value_type;
|
||||
typedef T* pointer;
|
||||
typedef T& reference;
|
||||
typedef std::random_access_iterator_tag iterator_category;
|
||||
iterator(T* ptr_) : ptr(ptr_) {}
|
||||
T& operator*() const { return *ptr; }
|
||||
T* operator->() const { return ptr; }
|
||||
T& operator[](size_type pos) { return ptr[pos]; }
|
||||
const T& operator[](size_type pos) const { return ptr[pos]; }
|
||||
iterator& operator++() { ptr++; return *this; }
|
||||
iterator& operator--() { ptr--; return *this; }
|
||||
iterator operator++(int) { iterator copy(*this); ++(*this); return copy; }
|
||||
iterator operator--(int) { iterator copy(*this); --(*this); return copy; }
|
||||
difference_type friend operator-(iterator a, iterator b) { return (&(*a) - &(*b)); }
|
||||
iterator operator+(size_type n) { return iterator(ptr + n); }
|
||||
iterator& operator+=(size_type n) { ptr += n; return *this; }
|
||||
iterator operator-(size_type n) { return iterator(ptr - n); }
|
||||
iterator& operator-=(size_type n) { ptr -= n; return *this; }
|
||||
bool operator==(iterator x) const { return ptr == x.ptr; }
|
||||
bool operator!=(iterator x) const { return ptr != x.ptr; }
|
||||
bool operator>=(iterator x) const { return ptr >= x.ptr; }
|
||||
bool operator<=(iterator x) const { return ptr <= x.ptr; }
|
||||
bool operator>(iterator x) const { return ptr > x.ptr; }
|
||||
bool operator<(iterator x) const { return ptr < x.ptr; }
|
||||
};
|
||||
|
||||
class reverse_iterator {
|
||||
T* ptr;
|
||||
public:
|
||||
typedef Diff difference_type;
|
||||
typedef T value_type;
|
||||
typedef T* pointer;
|
||||
typedef T& reference;
|
||||
typedef std::bidirectional_iterator_tag iterator_category;
|
||||
reverse_iterator(T* ptr_) : ptr(ptr_) {}
|
||||
T& operator*() { return *ptr; }
|
||||
const T& operator*() const { return *ptr; }
|
||||
T* operator->() { return ptr; }
|
||||
const T* operator->() const { return ptr; }
|
||||
reverse_iterator& operator--() { ptr++; return *this; }
|
||||
reverse_iterator& operator++() { ptr--; return *this; }
|
||||
reverse_iterator operator++(int) { reverse_iterator copy(*this); ++(*this); return copy; }
|
||||
reverse_iterator operator--(int) { reverse_iterator copy(*this); --(*this); return copy; }
|
||||
bool operator==(reverse_iterator x) const { return ptr == x.ptr; }
|
||||
bool operator!=(reverse_iterator x) const { return ptr != x.ptr; }
|
||||
};
|
||||
|
||||
class const_iterator {
|
||||
const T* ptr;
|
||||
public:
|
||||
typedef Diff difference_type;
|
||||
typedef const T value_type;
|
||||
typedef const T* pointer;
|
||||
typedef const T& reference;
|
||||
typedef std::random_access_iterator_tag iterator_category;
|
||||
const_iterator(const T* ptr_) : ptr(ptr_) {}
|
||||
const_iterator(iterator x) : ptr(&(*x)) {}
|
||||
const T& operator*() const { return *ptr; }
|
||||
const T* operator->() const { return ptr; }
|
||||
const T& operator[](size_type pos) const { return ptr[pos]; }
|
||||
const_iterator& operator++() { ptr++; return *this; }
|
||||
const_iterator& operator--() { ptr--; return *this; }
|
||||
const_iterator operator++(int) { const_iterator copy(*this); ++(*this); return copy; }
|
||||
const_iterator operator--(int) { const_iterator copy(*this); --(*this); return copy; }
|
||||
difference_type friend operator-(const_iterator a, const_iterator b) { return (&(*a) - &(*b)); }
|
||||
const_iterator operator+(size_type n) { return const_iterator(ptr + n); }
|
||||
const_iterator& operator+=(size_type n) { ptr += n; return *this; }
|
||||
const_iterator operator-(size_type n) { return const_iterator(ptr - n); }
|
||||
const_iterator& operator-=(size_type n) { ptr -= n; return *this; }
|
||||
bool operator==(const_iterator x) const { return ptr == x.ptr; }
|
||||
bool operator!=(const_iterator x) const { return ptr != x.ptr; }
|
||||
bool operator>=(const_iterator x) const { return ptr >= x.ptr; }
|
||||
bool operator<=(const_iterator x) const { return ptr <= x.ptr; }
|
||||
bool operator>(const_iterator x) const { return ptr > x.ptr; }
|
||||
bool operator<(const_iterator x) const { return ptr < x.ptr; }
|
||||
};
|
||||
|
||||
class const_reverse_iterator {
|
||||
const T* ptr;
|
||||
public:
|
||||
typedef Diff difference_type;
|
||||
typedef const T value_type;
|
||||
typedef const T* pointer;
|
||||
typedef const T& reference;
|
||||
typedef std::bidirectional_iterator_tag iterator_category;
|
||||
const_reverse_iterator(T* ptr_) : ptr(ptr_) {}
|
||||
const_reverse_iterator(reverse_iterator x) : ptr(&(*x)) {}
|
||||
const T& operator*() const { return *ptr; }
|
||||
const T* operator->() const { return ptr; }
|
||||
const_reverse_iterator& operator--() { ptr++; return *this; }
|
||||
const_reverse_iterator& operator++() { ptr--; return *this; }
|
||||
const_reverse_iterator operator++(int) { const_reverse_iterator copy(*this); ++(*this); return copy; }
|
||||
const_reverse_iterator operator--(int) { const_reverse_iterator copy(*this); --(*this); return copy; }
|
||||
bool operator==(const_reverse_iterator x) const { return ptr == x.ptr; }
|
||||
bool operator!=(const_reverse_iterator x) const { return ptr != x.ptr; }
|
||||
};
|
||||
|
||||
private:
|
||||
size_type _size;
|
||||
union {
|
||||
char direct[sizeof(T) * N];
|
||||
struct {
|
||||
size_type capacity;
|
||||
char* indirect;
|
||||
};
|
||||
} _union;
|
||||
|
||||
T* direct_ptr(difference_type pos) { return reinterpret_cast<T*>(_union.direct) + pos; }
|
||||
const T* direct_ptr(difference_type pos) const { return reinterpret_cast<const T*>(_union.direct) + pos; }
|
||||
T* indirect_ptr(difference_type pos) { return reinterpret_cast<T*>(_union.indirect) + pos; }
|
||||
const T* indirect_ptr(difference_type pos) const { return reinterpret_cast<const T*>(_union.indirect) + pos; }
|
||||
bool is_direct() const { return _size <= N; }
|
||||
|
||||
void change_capacity(size_type new_capacity) {
|
||||
if (new_capacity <= N) {
|
||||
if (!is_direct()) {
|
||||
T* indirect = indirect_ptr(0);
|
||||
T* src = indirect;
|
||||
T* dst = direct_ptr(0);
|
||||
memcpy(dst, src, size() * sizeof(T));
|
||||
free(indirect);
|
||||
_size -= N + 1;
|
||||
}
|
||||
} else {
|
||||
if (!is_direct()) {
|
||||
/* FIXME: Because malloc/realloc here won't call new_handler if allocation fails, assert
|
||||
success. These should instead use an allocator or new/delete so that handlers
|
||||
are called as necessary, but performance would be slightly degraded by doing so. */
|
||||
_union.indirect = static_cast<char*>(realloc(_union.indirect, ((size_t)sizeof(T)) * new_capacity));
|
||||
if (!_union.indirect) { new_handler_terminate(); }
|
||||
_union.capacity = new_capacity;
|
||||
} else {
|
||||
char* new_indirect = static_cast<char*>(malloc(((size_t)sizeof(T)) * new_capacity));
|
||||
if (!new_indirect) { new_handler_terminate(); }
|
||||
T* src = direct_ptr(0);
|
||||
T* dst = reinterpret_cast<T*>(new_indirect);
|
||||
memcpy(dst, src, size() * sizeof(T));
|
||||
_union.indirect = new_indirect;
|
||||
_union.capacity = new_capacity;
|
||||
_size += N + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
T* item_ptr(difference_type pos) { return is_direct() ? direct_ptr(pos) : indirect_ptr(pos); }
|
||||
const T* item_ptr(difference_type pos) const { return is_direct() ? direct_ptr(pos) : indirect_ptr(pos); }
|
||||
|
||||
public:
|
||||
void assign(size_type n, const T& val) {
|
||||
clear();
|
||||
if (capacity() < n) {
|
||||
change_capacity(n);
|
||||
}
|
||||
while (size() < n) {
|
||||
_size++;
|
||||
new(static_cast<void*>(item_ptr(size() - 1))) T(val);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename InputIterator>
|
||||
void assign(InputIterator first, InputIterator last) {
|
||||
size_type n = last - first;
|
||||
clear();
|
||||
if (capacity() < n) {
|
||||
change_capacity(n);
|
||||
}
|
||||
while (first != last) {
|
||||
_size++;
|
||||
new(static_cast<void*>(item_ptr(size() - 1))) T(*first);
|
||||
++first;
|
||||
}
|
||||
}
|
||||
|
||||
prevector() : _size(0) {}
|
||||
|
||||
explicit prevector(size_type n) : _size(0) {
|
||||
resize(n);
|
||||
}
|
||||
|
||||
explicit prevector(size_type n, const T& val = T()) : _size(0) {
|
||||
change_capacity(n);
|
||||
while (size() < n) {
|
||||
_size++;
|
||||
new(static_cast<void*>(item_ptr(size() - 1))) T(val);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename InputIterator>
|
||||
prevector(InputIterator first, InputIterator last) : _size(0) {
|
||||
size_type n = last - first;
|
||||
change_capacity(n);
|
||||
while (first != last) {
|
||||
_size++;
|
||||
new(static_cast<void*>(item_ptr(size() - 1))) T(*first);
|
||||
++first;
|
||||
}
|
||||
}
|
||||
|
||||
prevector(const prevector<N, T, Size, Diff>& other) : _size(0) {
|
||||
change_capacity(other.size());
|
||||
const_iterator it = other.begin();
|
||||
while (it != other.end()) {
|
||||
_size++;
|
||||
new(static_cast<void*>(item_ptr(size() - 1))) T(*it);
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
prevector& operator=(const prevector<N, T, Size, Diff>& other) {
|
||||
if (&other == this) {
|
||||
return *this;
|
||||
}
|
||||
resize(0);
|
||||
change_capacity(other.size());
|
||||
const_iterator it = other.begin();
|
||||
while (it != other.end()) {
|
||||
_size++;
|
||||
new(static_cast<void*>(item_ptr(size() - 1))) T(*it);
|
||||
++it;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
size_type size() const {
|
||||
return is_direct() ? _size : _size - N - 1;
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
iterator begin() { return iterator(item_ptr(0)); }
|
||||
const_iterator begin() const { return const_iterator(item_ptr(0)); }
|
||||
iterator end() { return iterator(item_ptr(size())); }
|
||||
const_iterator end() const { return const_iterator(item_ptr(size())); }
|
||||
|
||||
reverse_iterator rbegin() { return reverse_iterator(item_ptr(size() - 1)); }
|
||||
const_reverse_iterator rbegin() const { return const_reverse_iterator(item_ptr(size() - 1)); }
|
||||
reverse_iterator rend() { return reverse_iterator(item_ptr(-1)); }
|
||||
const_reverse_iterator rend() const { return const_reverse_iterator(item_ptr(-1)); }
|
||||
|
||||
size_t capacity() const {
|
||||
if (is_direct()) {
|
||||
return N;
|
||||
} else {
|
||||
return _union.capacity;
|
||||
}
|
||||
}
|
||||
|
||||
T& operator[](size_type pos) {
|
||||
return *item_ptr(pos);
|
||||
}
|
||||
|
||||
const T& operator[](size_type pos) const {
|
||||
return *item_ptr(pos);
|
||||
}
|
||||
|
||||
void resize(size_type new_size) {
|
||||
while (size() > new_size) {
|
||||
item_ptr(size() - 1)->~T();
|
||||
_size--;
|
||||
}
|
||||
if (new_size > capacity()) {
|
||||
change_capacity(new_size);
|
||||
}
|
||||
while (size() < new_size) {
|
||||
_size++;
|
||||
new(static_cast<void*>(item_ptr(size() - 1))) T();
|
||||
}
|
||||
}
|
||||
|
||||
void reserve(size_type new_capacity) {
|
||||
if (new_capacity > capacity()) {
|
||||
change_capacity(new_capacity);
|
||||
}
|
||||
}
|
||||
|
||||
void shrink_to_fit() {
|
||||
change_capacity(size());
|
||||
}
|
||||
|
||||
void clear() {
|
||||
resize(0);
|
||||
}
|
||||
|
||||
iterator insert(iterator pos, const T& value) {
|
||||
size_type p = pos - begin();
|
||||
size_type new_size = size() + 1;
|
||||
if (capacity() < new_size) {
|
||||
change_capacity(new_size + (new_size >> 1));
|
||||
}
|
||||
memmove(item_ptr(p + 1), item_ptr(p), (size() - p) * sizeof(T));
|
||||
_size++;
|
||||
new(static_cast<void*>(item_ptr(p))) T(value);
|
||||
return iterator(item_ptr(p));
|
||||
}
|
||||
|
||||
void insert(iterator pos, size_type count, const T& value) {
|
||||
size_type p = pos - begin();
|
||||
size_type new_size = size() + count;
|
||||
if (capacity() < new_size) {
|
||||
change_capacity(new_size + (new_size >> 1));
|
||||
}
|
||||
memmove(item_ptr(p + count), item_ptr(p), (size() - p) * sizeof(T));
|
||||
_size += count;
|
||||
for (size_type i = 0; i < count; i++) {
|
||||
new(static_cast<void*>(item_ptr(p + i))) T(value);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename InputIterator>
|
||||
void insert(iterator pos, InputIterator first, InputIterator last) {
|
||||
size_type p = pos - begin();
|
||||
difference_type count = last - first;
|
||||
size_type new_size = size() + count;
|
||||
if (capacity() < new_size) {
|
||||
change_capacity(new_size + (new_size >> 1));
|
||||
}
|
||||
memmove(item_ptr(p + count), item_ptr(p), (size() - p) * sizeof(T));
|
||||
_size += count;
|
||||
while (first != last) {
|
||||
new(static_cast<void*>(item_ptr(p))) T(*first);
|
||||
++p;
|
||||
++first;
|
||||
}
|
||||
}
|
||||
|
||||
iterator erase(iterator pos) {
|
||||
(*pos).~T();
|
||||
memmove(&(*pos), &(*pos) + 1, ((char*)&(*end())) - ((char*)(1 + &(*pos))));
|
||||
_size--;
|
||||
return pos;
|
||||
}
|
||||
|
||||
iterator erase(iterator first, iterator last) {
|
||||
iterator p = first;
|
||||
char* endp = (char*)&(*end());
|
||||
while (p != last) {
|
||||
(*p).~T();
|
||||
_size--;
|
||||
++p;
|
||||
}
|
||||
memmove(&(*first), &(*last), endp - ((char*)(&(*last))));
|
||||
return first;
|
||||
}
|
||||
|
||||
void push_back(const T& value) {
|
||||
size_type new_size = size() + 1;
|
||||
if (capacity() < new_size) {
|
||||
change_capacity(new_size + (new_size >> 1));
|
||||
}
|
||||
new(item_ptr(size())) T(value);
|
||||
_size++;
|
||||
}
|
||||
|
||||
void pop_back() {
|
||||
_size--;
|
||||
}
|
||||
|
||||
T& front() {
|
||||
return *item_ptr(0);
|
||||
}
|
||||
|
||||
const T& front() const {
|
||||
return *item_ptr(0);
|
||||
}
|
||||
|
||||
T& back() {
|
||||
return *item_ptr(size() - 1);
|
||||
}
|
||||
|
||||
const T& back() const {
|
||||
return *item_ptr(size() - 1);
|
||||
}
|
||||
|
||||
void swap(prevector<N, T, Size, Diff>& other) {
|
||||
if (_size & other._size & 1) {
|
||||
std::swap(_union.capacity, other._union.capacity);
|
||||
std::swap(_union.indirect, other._union.indirect);
|
||||
} else {
|
||||
std::swap(_union, other._union);
|
||||
}
|
||||
std::swap(_size, other._size);
|
||||
}
|
||||
|
||||
~prevector() {
|
||||
clear();
|
||||
if (!is_direct()) {
|
||||
free(_union.indirect);
|
||||
_union.indirect = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(const prevector<N, T, Size, Diff>& other) const {
|
||||
if (other.size() != size()) {
|
||||
return false;
|
||||
}
|
||||
const_iterator b1 = begin();
|
||||
const_iterator b2 = other.begin();
|
||||
const_iterator e1 = end();
|
||||
while (b1 != e1) {
|
||||
if ((*b1) != (*b2)) {
|
||||
return false;
|
||||
}
|
||||
++b1;
|
||||
++b2;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator!=(const prevector<N, T, Size, Diff>& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool operator<(const prevector<N, T, Size, Diff>& other) const {
|
||||
if (size() < other.size()) {
|
||||
return true;
|
||||
}
|
||||
if (size() > other.size()) {
|
||||
return false;
|
||||
}
|
||||
const_iterator b1 = begin();
|
||||
const_iterator b2 = other.begin();
|
||||
const_iterator e1 = end();
|
||||
while (b1 != e1) {
|
||||
if ((*b1) < (*b2)) {
|
||||
return true;
|
||||
}
|
||||
if ((*b2) < (*b1)) {
|
||||
return false;
|
||||
}
|
||||
++b1;
|
||||
++b2;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t allocated_memory() const {
|
||||
if (is_direct()) {
|
||||
return 0;
|
||||
} else {
|
||||
return ((size_t)(sizeof(T))) * _union.capacity;
|
||||
}
|
||||
}
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
#endif
|
||||
@@ -202,12 +202,12 @@ uint256 CBlock::CheckMerkleBranch(uint256 hash, const std::vector<uint256>& vMer
|
||||
std::string CBlock::ToString() const
|
||||
{
|
||||
std::stringstream s;
|
||||
s << strprintf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, hashReserved=%s, nTime=%u, nBits=%08x, nNonce=%s, vtx=%u)\n",
|
||||
s << strprintf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, hashFinalSaplingRoot=%s, nTime=%u, nBits=%08x, nNonce=%s, vtx=%u)\n",
|
||||
GetHash().ToString(),
|
||||
nVersion,
|
||||
hashPrevBlock.ToString(),
|
||||
hashMerkleRoot.ToString(),
|
||||
hashReserved.ToString(),
|
||||
hashFinalSaplingRoot.ToString(),
|
||||
nTime, nBits, nNonce.ToString(),
|
||||
vtx.size());
|
||||
for (unsigned int i = 0; i < vtx.size(); i++)
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
int32_t nVersion;
|
||||
uint256 hashPrevBlock;
|
||||
uint256 hashMerkleRoot;
|
||||
uint256 hashReserved;
|
||||
uint256 hashFinalSaplingRoot;
|
||||
uint32_t nTime;
|
||||
uint32_t nBits;
|
||||
CPOSNonce nNonce;
|
||||
@@ -45,12 +45,11 @@ public:
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(this->nVersion);
|
||||
nVersion = this->nVersion;
|
||||
READWRITE(hashPrevBlock);
|
||||
READWRITE(hashMerkleRoot);
|
||||
READWRITE(hashReserved);
|
||||
READWRITE(hashFinalSaplingRoot);
|
||||
READWRITE(nTime);
|
||||
READWRITE(nBits);
|
||||
READWRITE(nNonce);
|
||||
@@ -62,7 +61,7 @@ public:
|
||||
nVersion = CBlockHeader::CURRENT_VERSION;
|
||||
hashPrevBlock.SetNull();
|
||||
hashMerkleRoot.SetNull();
|
||||
hashReserved.SetNull();
|
||||
hashFinalSaplingRoot.SetNull();
|
||||
nTime = 0;
|
||||
nBits = 0;
|
||||
nNonce = uint256();
|
||||
@@ -156,7 +155,7 @@ class CNetworkBlockHeader : public CBlockHeader
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(*(CBlockHeader*)this);
|
||||
READWRITE(compatVec);
|
||||
}
|
||||
@@ -191,7 +190,7 @@ public:
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(*(CBlockHeader*)this);
|
||||
READWRITE(vtx);
|
||||
}
|
||||
@@ -209,7 +208,7 @@ public:
|
||||
block.nVersion = nVersion;
|
||||
block.hashPrevBlock = hashPrevBlock;
|
||||
block.hashMerkleRoot = hashMerkleRoot;
|
||||
block.hashReserved = hashReserved;
|
||||
block.hashFinalSaplingRoot = hashFinalSaplingRoot;
|
||||
block.nTime = nTime;
|
||||
block.nBits = nBits;
|
||||
block.nNonce = nNonce;
|
||||
@@ -251,12 +250,11 @@ public:
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(this->nVersion);
|
||||
nVersion = this->nVersion;
|
||||
READWRITE(hashPrevBlock);
|
||||
READWRITE(hashMerkleRoot);
|
||||
READWRITE(hashReserved);
|
||||
READWRITE(hashFinalSaplingRoot);
|
||||
READWRITE(nTime);
|
||||
READWRITE(nBits);
|
||||
}
|
||||
@@ -281,8 +279,9 @@ struct CBlockLocator
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
if (!(nType & SER_GETHASH))
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
int nVersion = s.GetVersion();
|
||||
if (!(s.GetType() & SER_GETHASH))
|
||||
READWRITE(nVersion);
|
||||
READWRITE(vHave);
|
||||
}
|
||||
|
||||
@@ -9,26 +9,31 @@
|
||||
#include "tinyformat.h"
|
||||
#include "utilstrencodings.h"
|
||||
|
||||
JSDescription::JSDescription(ZCJoinSplit& params,
|
||||
const uint256& pubKeyHash,
|
||||
const uint256& anchor,
|
||||
const boost::array<libzcash::JSInput, ZC_NUM_JS_INPUTS>& inputs,
|
||||
const boost::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS>& outputs,
|
||||
CAmount vpub_old,
|
||||
CAmount vpub_new,
|
||||
bool computeProof,
|
||||
uint256 *esk // payment disclosure
|
||||
) : vpub_old(vpub_old), vpub_new(vpub_new), anchor(anchor)
|
||||
#include "librustzcash.h"
|
||||
|
||||
JSDescription::JSDescription(
|
||||
bool makeGrothProof,
|
||||
ZCJoinSplit& params,
|
||||
const uint256& joinSplitPubKey,
|
||||
const uint256& anchor,
|
||||
const std::array<libzcash::JSInput, ZC_NUM_JS_INPUTS>& inputs,
|
||||
const std::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS>& outputs,
|
||||
CAmount vpub_old,
|
||||
CAmount vpub_new,
|
||||
bool computeProof,
|
||||
uint256 *esk // payment disclosure
|
||||
) : vpub_old(vpub_old), vpub_new(vpub_new), anchor(anchor)
|
||||
{
|
||||
boost::array<libzcash::Note, ZC_NUM_JS_OUTPUTS> notes;
|
||||
std::array<libzcash::SproutNote, ZC_NUM_JS_OUTPUTS> notes;
|
||||
|
||||
proof = params.prove(
|
||||
makeGrothProof,
|
||||
inputs,
|
||||
outputs,
|
||||
notes,
|
||||
ciphertexts,
|
||||
ephemeralKey,
|
||||
pubKeyHash,
|
||||
joinSplitPubKey,
|
||||
randomSeed,
|
||||
macs,
|
||||
nullifiers,
|
||||
@@ -42,24 +47,20 @@ JSDescription::JSDescription(ZCJoinSplit& params,
|
||||
}
|
||||
|
||||
JSDescription JSDescription::Randomized(
|
||||
ZCJoinSplit& params,
|
||||
const uint256& pubKeyHash,
|
||||
const uint256& anchor,
|
||||
boost::array<libzcash::JSInput, ZC_NUM_JS_INPUTS>& inputs,
|
||||
boost::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS>& outputs,
|
||||
#ifdef __LP64__
|
||||
boost::array<uint64_t, ZC_NUM_JS_INPUTS>& inputMap,
|
||||
boost::array<uint64_t, ZC_NUM_JS_OUTPUTS>& outputMap,
|
||||
#else
|
||||
boost::array<size_t, ZC_NUM_JS_INPUTS>& inputMap,
|
||||
boost::array<size_t, ZC_NUM_JS_OUTPUTS>& outputMap,
|
||||
#endif
|
||||
CAmount vpub_old,
|
||||
CAmount vpub_new,
|
||||
bool computeProof,
|
||||
uint256 *esk, // payment disclosure
|
||||
std::function<int(int)> gen
|
||||
)
|
||||
bool makeGrothProof,
|
||||
ZCJoinSplit& params,
|
||||
const uint256& joinSplitPubKey,
|
||||
const uint256& anchor,
|
||||
std::array<libzcash::JSInput, ZC_NUM_JS_INPUTS>& inputs,
|
||||
std::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS>& outputs,
|
||||
std::array<size_t, ZC_NUM_JS_INPUTS>& inputMap,
|
||||
std::array<size_t, ZC_NUM_JS_OUTPUTS>& outputMap,
|
||||
CAmount vpub_old,
|
||||
CAmount vpub_new,
|
||||
bool computeProof,
|
||||
uint256 *esk, // payment disclosure
|
||||
std::function<int(int)> gen
|
||||
)
|
||||
{
|
||||
// Randomize the order of the inputs and outputs
|
||||
inputMap = {0, 1};
|
||||
@@ -71,34 +72,76 @@ JSDescription JSDescription::Randomized(
|
||||
MappedShuffle(outputs.begin(), outputMap.begin(), ZC_NUM_JS_OUTPUTS, gen);
|
||||
|
||||
return JSDescription(
|
||||
params, pubKeyHash, anchor, inputs, outputs,
|
||||
makeGrothProof,
|
||||
params, joinSplitPubKey, anchor, inputs, outputs,
|
||||
vpub_old, vpub_new, computeProof,
|
||||
esk // payment disclosure
|
||||
);
|
||||
}
|
||||
|
||||
class SproutProofVerifier : public boost::static_visitor<bool>
|
||||
{
|
||||
ZCJoinSplit& params;
|
||||
libzcash::ProofVerifier& verifier;
|
||||
const uint256& joinSplitPubKey;
|
||||
const JSDescription& jsdesc;
|
||||
|
||||
public:
|
||||
SproutProofVerifier(
|
||||
ZCJoinSplit& params,
|
||||
libzcash::ProofVerifier& verifier,
|
||||
const uint256& joinSplitPubKey,
|
||||
const JSDescription& jsdesc
|
||||
) : params(params), jsdesc(jsdesc), verifier(verifier), joinSplitPubKey(joinSplitPubKey) {}
|
||||
|
||||
bool operator()(const libzcash::PHGRProof& proof) const
|
||||
{
|
||||
return params.verify(
|
||||
proof,
|
||||
verifier,
|
||||
joinSplitPubKey,
|
||||
jsdesc.randomSeed,
|
||||
jsdesc.macs,
|
||||
jsdesc.nullifiers,
|
||||
jsdesc.commitments,
|
||||
jsdesc.vpub_old,
|
||||
jsdesc.vpub_new,
|
||||
jsdesc.anchor
|
||||
);
|
||||
}
|
||||
|
||||
bool operator()(const libzcash::GrothProof& proof) const
|
||||
{
|
||||
uint256 h_sig = params.h_sig(jsdesc.randomSeed, jsdesc.nullifiers, joinSplitPubKey);
|
||||
|
||||
return librustzcash_sprout_verify(
|
||||
proof.begin(),
|
||||
jsdesc.anchor.begin(),
|
||||
h_sig.begin(),
|
||||
jsdesc.macs[0].begin(),
|
||||
jsdesc.macs[1].begin(),
|
||||
jsdesc.nullifiers[0].begin(),
|
||||
jsdesc.nullifiers[1].begin(),
|
||||
jsdesc.commitments[0].begin(),
|
||||
jsdesc.commitments[1].begin(),
|
||||
jsdesc.vpub_old,
|
||||
jsdesc.vpub_new
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
bool JSDescription::Verify(
|
||||
ZCJoinSplit& params,
|
||||
libzcash::ProofVerifier& verifier,
|
||||
const uint256& pubKeyHash
|
||||
const uint256& joinSplitPubKey
|
||||
) const {
|
||||
return params.verify(
|
||||
proof,
|
||||
verifier,
|
||||
pubKeyHash,
|
||||
randomSeed,
|
||||
macs,
|
||||
nullifiers,
|
||||
commitments,
|
||||
vpub_old,
|
||||
vpub_new,
|
||||
anchor
|
||||
);
|
||||
auto pv = SproutProofVerifier(params, verifier, joinSplitPubKey, *this);
|
||||
return boost::apply_visitor(pv, proof);
|
||||
}
|
||||
|
||||
uint256 JSDescription::h_sig(ZCJoinSplit& params, const uint256& pubKeyHash) const
|
||||
uint256 JSDescription::h_sig(ZCJoinSplit& params, const uint256& joinSplitPubKey) const
|
||||
{
|
||||
return params.h_sig(randomSeed, nullifiers, pubKeyHash);
|
||||
return params.h_sig(randomSeed, nullifiers, joinSplitPubKey);
|
||||
}
|
||||
|
||||
std::string COutPoint::ToString() const
|
||||
@@ -106,6 +149,11 @@ std::string COutPoint::ToString() const
|
||||
return strprintf("COutPoint(%s, %u)", hash.ToString().substr(0,10), n);
|
||||
}
|
||||
|
||||
std::string SaplingOutPoint::ToString() const
|
||||
{
|
||||
return strprintf("SaplingOutPoint(%s, %u)", hash.ToString().substr(0, 10), n);
|
||||
}
|
||||
|
||||
CTxIn::CTxIn(COutPoint prevoutIn, CScript scriptSigIn, uint32_t nSequenceIn)
|
||||
{
|
||||
prevout = prevoutIn;
|
||||
@@ -128,7 +176,7 @@ std::string CTxIn::ToString() const
|
||||
if (prevout.IsNull())
|
||||
str += strprintf(", coinbase %s", HexStr(scriptSig));
|
||||
else
|
||||
str += strprintf(", scriptSig=%s", scriptSig.ToString().substr(0,24));
|
||||
str += strprintf(", scriptSig=%s", HexStr(scriptSig).substr(0, 24));
|
||||
if (nSequence != std::numeric_limits<unsigned int>::max())
|
||||
str += strprintf(", nSequence=%u", nSequence);
|
||||
str += ")";
|
||||
@@ -148,13 +196,15 @@ uint256 CTxOut::GetHash() const
|
||||
|
||||
std::string CTxOut::ToString() const
|
||||
{
|
||||
return strprintf("CTxOut(nValue=%d.%08d, scriptPubKey=%s)", nValue / COIN, nValue % COIN, scriptPubKey.ToString().substr(0,30));
|
||||
return strprintf("CTxOut(nValue=%d.%08d, scriptPubKey=%s)", nValue / COIN, nValue % COIN, HexStr(scriptPubKey).substr(0, 30));
|
||||
}
|
||||
|
||||
CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::SPROUT_MIN_CURRENT_VERSION), fOverwintered(false), nVersionGroupId(0), nExpiryHeight(0), nLockTime(0) {}
|
||||
CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::SPROUT_MIN_CURRENT_VERSION), fOverwintered(false), nVersionGroupId(0), nExpiryHeight(0), nLockTime(0), valueBalance(0) {}
|
||||
CMutableTransaction::CMutableTransaction(const CTransaction& tx) : nVersion(tx.nVersion), fOverwintered(tx.fOverwintered), nVersionGroupId(tx.nVersionGroupId), nExpiryHeight(tx.nExpiryHeight),
|
||||
vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime),
|
||||
vjoinsplit(tx.vjoinsplit), joinSplitPubKey(tx.joinSplitPubKey), joinSplitSig(tx.joinSplitSig)
|
||||
valueBalance(tx.valueBalance), vShieldedSpend(tx.vShieldedSpend), vShieldedOutput(tx.vShieldedOutput),
|
||||
vjoinsplit(tx.vjoinsplit), joinSplitPubKey(tx.joinSplitPubKey), joinSplitSig(tx.joinSplitSig),
|
||||
bindingSig(tx.bindingSig)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -169,11 +219,13 @@ void CTransaction::UpdateHash() const
|
||||
*const_cast<uint256*>(&hash) = SerializeHash(*this);
|
||||
}
|
||||
|
||||
CTransaction::CTransaction() : nVersion(CTransaction::SPROUT_MIN_CURRENT_VERSION), fOverwintered(false), nVersionGroupId(0), nExpiryHeight(0), vin(), vout(), nLockTime(0), vjoinsplit(), joinSplitPubKey(), joinSplitSig() { }
|
||||
CTransaction::CTransaction() : nVersion(CTransaction::SPROUT_MIN_CURRENT_VERSION), fOverwintered(false), nVersionGroupId(0), nExpiryHeight(0), vin(), vout(), nLockTime(0), valueBalance(0), vShieldedSpend(), vShieldedOutput(), vjoinsplit(), joinSplitPubKey(), joinSplitSig(), bindingSig() { }
|
||||
|
||||
CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), fOverwintered(tx.fOverwintered), nVersionGroupId(tx.nVersionGroupId), nExpiryHeight(tx.nExpiryHeight),
|
||||
vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime),
|
||||
vjoinsplit(tx.vjoinsplit), joinSplitPubKey(tx.joinSplitPubKey), joinSplitSig(tx.joinSplitSig)
|
||||
valueBalance(tx.valueBalance), vShieldedSpend(tx.vShieldedSpend), vShieldedOutput(tx.vShieldedOutput),
|
||||
vjoinsplit(tx.vjoinsplit), joinSplitPubKey(tx.joinSplitPubKey), joinSplitSig(tx.joinSplitSig),
|
||||
bindingSig(tx.bindingSig)
|
||||
{
|
||||
UpdateHash();
|
||||
}
|
||||
@@ -184,11 +236,23 @@ CTransaction::CTransaction(
|
||||
const CMutableTransaction &tx,
|
||||
bool evilDeveloperFlag) : nVersion(tx.nVersion), fOverwintered(tx.fOverwintered), nVersionGroupId(tx.nVersionGroupId), nExpiryHeight(tx.nExpiryHeight),
|
||||
vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime),
|
||||
vjoinsplit(tx.vjoinsplit), joinSplitPubKey(tx.joinSplitPubKey), joinSplitSig(tx.joinSplitSig)
|
||||
valueBalance(tx.valueBalance), vShieldedSpend(tx.vShieldedSpend), vShieldedOutput(tx.vShieldedOutput),
|
||||
vjoinsplit(tx.vjoinsplit), joinSplitPubKey(tx.joinSplitPubKey), joinSplitSig(tx.joinSplitSig),
|
||||
bindingSig(tx.bindingSig)
|
||||
{
|
||||
assert(evilDeveloperFlag);
|
||||
}
|
||||
|
||||
CTransaction::CTransaction(CMutableTransaction &&tx) : nVersion(tx.nVersion), fOverwintered(tx.fOverwintered), nVersionGroupId(tx.nVersionGroupId),
|
||||
vin(std::move(tx.vin)), vout(std::move(tx.vout)), nLockTime(tx.nLockTime), nExpiryHeight(tx.nExpiryHeight),
|
||||
valueBalance(tx.valueBalance),
|
||||
vShieldedSpend(std::move(tx.vShieldedSpend)), vShieldedOutput(std::move(tx.vShieldedOutput)),
|
||||
vjoinsplit(std::move(tx.vjoinsplit)),
|
||||
joinSplitPubKey(std::move(tx.joinSplitPubKey)), joinSplitSig(std::move(tx.joinSplitSig))
|
||||
{
|
||||
UpdateHash();
|
||||
}
|
||||
|
||||
CTransaction& CTransaction::operator=(const CTransaction &tx) {
|
||||
*const_cast<bool*>(&fOverwintered) = tx.fOverwintered;
|
||||
*const_cast<int*>(&nVersion) = tx.nVersion;
|
||||
@@ -197,9 +261,13 @@ CTransaction& CTransaction::operator=(const CTransaction &tx) {
|
||||
*const_cast<std::vector<CTxOut>*>(&vout) = tx.vout;
|
||||
*const_cast<unsigned int*>(&nLockTime) = tx.nLockTime;
|
||||
*const_cast<uint32_t*>(&nExpiryHeight) = tx.nExpiryHeight;
|
||||
*const_cast<CAmount*>(&valueBalance) = tx.valueBalance;
|
||||
*const_cast<std::vector<SpendDescription>*>(&vShieldedSpend) = tx.vShieldedSpend;
|
||||
*const_cast<std::vector<OutputDescription>*>(&vShieldedOutput) = tx.vShieldedOutput;
|
||||
*const_cast<std::vector<JSDescription>*>(&vjoinsplit) = tx.vjoinsplit;
|
||||
*const_cast<uint256*>(&joinSplitPubKey) = tx.joinSplitPubKey;
|
||||
*const_cast<joinsplit_sig_t*>(&joinSplitSig) = tx.joinSplitSig;
|
||||
*const_cast<binding_sig_t*>(&bindingSig) = tx.bindingSig;
|
||||
*const_cast<uint256*>(&hash) = tx.hash;
|
||||
return *this;
|
||||
}
|
||||
@@ -214,9 +282,18 @@ CAmount CTransaction::GetValueOut() const
|
||||
throw std::runtime_error("CTransaction::GetValueOut(): value out of range");
|
||||
}
|
||||
|
||||
if (valueBalance <= 0) {
|
||||
// NB: negative valueBalance "takes" money from the transparent value pool just as outputs do
|
||||
nValueOut += -valueBalance;
|
||||
|
||||
if (!MoneyRange(-valueBalance) || !MoneyRange(nValueOut)) {
|
||||
throw std::runtime_error("CTransaction::GetValueOut(): value out of range");
|
||||
}
|
||||
}
|
||||
|
||||
for (std::vector<JSDescription>::const_iterator it(vjoinsplit.begin()); it != vjoinsplit.end(); ++it)
|
||||
{
|
||||
// NB: vpub_old "takes" money from the value pool just as outputs do
|
||||
// NB: vpub_old "takes" money from the transparent value pool just as outputs do
|
||||
nValueOut += it->vpub_old;
|
||||
|
||||
if (!MoneyRange(it->vpub_old) || !MoneyRange(nValueOut))
|
||||
@@ -225,31 +302,26 @@ CAmount CTransaction::GetValueOut() const
|
||||
return nValueOut;
|
||||
}
|
||||
|
||||
CAmount CTransaction::GetJoinSplitValueIn() const
|
||||
CAmount CTransaction::GetShieldedValueIn() const
|
||||
{
|
||||
CAmount nValue = 0;
|
||||
|
||||
if (valueBalance >= 0) {
|
||||
// NB: positive valueBalance "gives" money to the transparent value pool just as inputs do
|
||||
nValue += valueBalance;
|
||||
|
||||
if (!MoneyRange(valueBalance) || !MoneyRange(nValue)) {
|
||||
throw std::runtime_error("CTransaction::GetShieldedValueIn(): value out of range");
|
||||
}
|
||||
}
|
||||
|
||||
for (std::vector<JSDescription>::const_iterator it(vjoinsplit.begin()); it != vjoinsplit.end(); ++it)
|
||||
{
|
||||
// NB: vpub_new "gives" money to the value pool just as inputs do
|
||||
// NB: vpub_new "gives" money to the transparent value pool just as inputs do
|
||||
nValue += it->vpub_new;
|
||||
|
||||
if (!MoneyRange(it->vpub_new) || !MoneyRange(nValue))
|
||||
throw std::runtime_error("CTransaction::GetJoinSplitValueIn(): value out of range");
|
||||
}
|
||||
|
||||
return nValue;
|
||||
}
|
||||
|
||||
CAmount CTransaction::GetJoinSplitValueOut() const
|
||||
{
|
||||
CAmount nValue = 0;
|
||||
for (std::vector<JSDescription>::const_iterator it(vjoinsplit.begin()); it != vjoinsplit.end(); ++it)
|
||||
{
|
||||
// NB: vpub_new "gives" money to the value pool just as inputs do
|
||||
nValue += it->vpub_old;
|
||||
|
||||
if (!MoneyRange(it->vpub_old) || !MoneyRange(nValue))
|
||||
throw std::runtime_error("CTransaction::GetJoinSplitValueOut(): value out of range");
|
||||
throw std::runtime_error("CTransaction::GetShieldedValueIn(): value out of range");
|
||||
}
|
||||
|
||||
return nValue;
|
||||
@@ -321,6 +393,19 @@ std::string CTransaction::ToString() const
|
||||
vin.size(),
|
||||
vout.size(),
|
||||
nLockTime);
|
||||
} else if (nVersion >= SAPLING_MIN_TX_VERSION) {
|
||||
str += strprintf("CTransaction(hash=%s, ver=%d, fOverwintered=%d, nVersionGroupId=%08x, vin.size=%u, vout.size=%u, nLockTime=%u, nExpiryHeight=%u, valueBalance=%u, vShieldedSpend.size=%u, vShieldedOutput.size=%u)\n",
|
||||
GetHash().ToString().substr(0,10),
|
||||
nVersion,
|
||||
fOverwintered,
|
||||
nVersionGroupId,
|
||||
vin.size(),
|
||||
vout.size(),
|
||||
nLockTime,
|
||||
nExpiryHeight,
|
||||
valueBalance,
|
||||
vShieldedSpend.size(),
|
||||
vShieldedOutput.size());
|
||||
} else if (nVersion >= 3) {
|
||||
str += strprintf("CTransaction(hash=%s, ver=%d, fOverwintered=%d, nVersionGroupId=%08x, vin.size=%u, vout.size=%u, nLockTime=%u, nExpiryHeight=%u)\n",
|
||||
GetHash().ToString().substr(0,10),
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "random.h"
|
||||
#include "script/script.h"
|
||||
#include "serialize.h"
|
||||
#include "streams.h"
|
||||
#include "uint256.h"
|
||||
#include "arith_uint256.h"
|
||||
#include "consensus/consensus.h"
|
||||
@@ -20,7 +21,9 @@
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#include <boost/array.hpp>
|
||||
#include <array>
|
||||
|
||||
#include <boost/variant.hpp>
|
||||
|
||||
#include "zcash/NoteEncryption.hpp"
|
||||
#include "zcash/Zcash.h"
|
||||
@@ -29,6 +32,159 @@
|
||||
|
||||
extern uint32_t ASSETCHAINS_MAGIC;
|
||||
|
||||
// Overwinter transaction version
|
||||
static const int32_t OVERWINTER_TX_VERSION = 3;
|
||||
static_assert(OVERWINTER_TX_VERSION >= OVERWINTER_MIN_TX_VERSION,
|
||||
"Overwinter tx version must not be lower than minimum");
|
||||
static_assert(OVERWINTER_TX_VERSION <= OVERWINTER_MAX_TX_VERSION,
|
||||
"Overwinter tx version must not be higher than maximum");
|
||||
|
||||
// Sapling transaction version
|
||||
static const int32_t SAPLING_TX_VERSION = 4;
|
||||
static_assert(SAPLING_TX_VERSION >= SAPLING_MIN_TX_VERSION,
|
||||
"Sapling tx version must not be lower than minimum");
|
||||
static_assert(SAPLING_TX_VERSION <= SAPLING_MAX_TX_VERSION,
|
||||
"Sapling tx version must not be higher than maximum");
|
||||
|
||||
/**
|
||||
* A shielded input to a transaction. It contains data that describes a Spend transfer.
|
||||
*/
|
||||
class SpendDescription
|
||||
{
|
||||
public:
|
||||
typedef std::array<unsigned char, 64> spend_auth_sig_t;
|
||||
|
||||
uint256 cv; //!< A value commitment to the value of the input note.
|
||||
uint256 anchor; //!< A Merkle root of the Sapling note commitment tree at some block height in the past.
|
||||
uint256 nullifier; //!< The nullifier of the input note.
|
||||
uint256 rk; //!< The randomized public key for spendAuthSig.
|
||||
libzcash::GrothProof zkproof; //!< A zero-knowledge proof using the spend circuit.
|
||||
spend_auth_sig_t spendAuthSig; //!< A signature authorizing this spend.
|
||||
|
||||
SpendDescription() { }
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(cv);
|
||||
READWRITE(anchor);
|
||||
READWRITE(nullifier);
|
||||
READWRITE(rk);
|
||||
READWRITE(zkproof);
|
||||
READWRITE(spendAuthSig);
|
||||
}
|
||||
|
||||
friend bool operator==(const SpendDescription& a, const SpendDescription& b)
|
||||
{
|
||||
return (
|
||||
a.cv == b.cv &&
|
||||
a.anchor == b.anchor &&
|
||||
a.nullifier == b.nullifier &&
|
||||
a.rk == b.rk &&
|
||||
a.zkproof == b.zkproof &&
|
||||
a.spendAuthSig == b.spendAuthSig
|
||||
);
|
||||
}
|
||||
|
||||
friend bool operator!=(const SpendDescription& a, const SpendDescription& b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A shielded output to a transaction. It contains data that describes an Output transfer.
|
||||
*/
|
||||
class OutputDescription
|
||||
{
|
||||
public:
|
||||
uint256 cv; //!< A value commitment to the value of the output note.
|
||||
uint256 cm; //!< The note commitment for the output note.
|
||||
uint256 ephemeralKey; //!< A Jubjub public key.
|
||||
libzcash::SaplingEncCiphertext encCiphertext; //!< A ciphertext component for the encrypted output note.
|
||||
libzcash::SaplingOutCiphertext outCiphertext; //!< A ciphertext component for the encrypted output note.
|
||||
libzcash::GrothProof zkproof; //!< A zero-knowledge proof using the output circuit.
|
||||
|
||||
OutputDescription() { }
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(cv);
|
||||
READWRITE(cm);
|
||||
READWRITE(ephemeralKey);
|
||||
READWRITE(encCiphertext);
|
||||
READWRITE(outCiphertext);
|
||||
READWRITE(zkproof);
|
||||
}
|
||||
|
||||
friend bool operator==(const OutputDescription& a, const OutputDescription& b)
|
||||
{
|
||||
return (
|
||||
a.cv == b.cv &&
|
||||
a.cm == b.cm &&
|
||||
a.ephemeralKey == b.ephemeralKey &&
|
||||
a.encCiphertext == b.encCiphertext &&
|
||||
a.outCiphertext == b.outCiphertext &&
|
||||
a.zkproof == b.zkproof
|
||||
);
|
||||
}
|
||||
|
||||
friend bool operator!=(const OutputDescription& a, const OutputDescription& b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Stream>
|
||||
class SproutProofSerializer : public boost::static_visitor<>
|
||||
{
|
||||
Stream& s;
|
||||
bool useGroth;
|
||||
|
||||
public:
|
||||
SproutProofSerializer(Stream& s, bool useGroth) : s(s), useGroth(useGroth) {}
|
||||
|
||||
void operator()(const libzcash::PHGRProof& proof) const
|
||||
{
|
||||
if (useGroth) {
|
||||
throw std::ios_base::failure("Invalid Sprout proof for transaction format (expected GrothProof, found PHGRProof)");
|
||||
}
|
||||
::Serialize(s, proof);
|
||||
}
|
||||
|
||||
void operator()(const libzcash::GrothProof& proof) const
|
||||
{
|
||||
if (!useGroth) {
|
||||
throw std::ios_base::failure("Invalid Sprout proof for transaction format (expected PHGRProof, found GrothProof)");
|
||||
}
|
||||
::Serialize(s, proof);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Stream, typename T>
|
||||
inline void SerReadWriteSproutProof(Stream& s, const T& proof, bool useGroth, CSerActionSerialize ser_action)
|
||||
{
|
||||
auto ps = SproutProofSerializer<Stream>(s, useGroth);
|
||||
boost::apply_visitor(ps, proof);
|
||||
}
|
||||
|
||||
template<typename Stream, typename T>
|
||||
inline void SerReadWriteSproutProof(Stream& s, T& proof, bool useGroth, CSerActionUnserialize ser_action)
|
||||
{
|
||||
if (useGroth) {
|
||||
libzcash::GrothProof grothProof;
|
||||
::Unserialize(s, grothProof);
|
||||
proof = grothProof;
|
||||
} else {
|
||||
libzcash::PHGRProof pghrProof;
|
||||
::Unserialize(s, pghrProof);
|
||||
proof = pghrProof;
|
||||
}
|
||||
}
|
||||
|
||||
class JSDescription
|
||||
{
|
||||
public:
|
||||
@@ -47,14 +203,14 @@ public:
|
||||
// are derived from the secrets placed in the note
|
||||
// and the secret spend-authority key known by the
|
||||
// spender.
|
||||
boost::array<uint256, ZC_NUM_JS_INPUTS> nullifiers;
|
||||
std::array<uint256, ZC_NUM_JS_INPUTS> nullifiers;
|
||||
|
||||
// Note commitments are introduced into the commitment
|
||||
// tree, blinding the public about the values and
|
||||
// destinations involved in the JoinSplit. The presence of
|
||||
// a commitment in the note commitment tree is required
|
||||
// to spend it.
|
||||
boost::array<uint256, ZC_NUM_JS_OUTPUTS> commitments;
|
||||
std::array<uint256, ZC_NUM_JS_OUTPUTS> commitments;
|
||||
|
||||
// Ephemeral key
|
||||
uint256 ephemeralKey;
|
||||
@@ -63,7 +219,7 @@ public:
|
||||
// These contain trapdoors, values and other information
|
||||
// that the recipient needs, including a memo field. It
|
||||
// is encrypted using the scheme implemented in crypto/NoteEncryption.cpp
|
||||
boost::array<ZCNoteEncryption::Ciphertext, ZC_NUM_JS_OUTPUTS> ciphertexts = {{ {{0}} }};
|
||||
std::array<ZCNoteEncryption::Ciphertext, ZC_NUM_JS_OUTPUTS> ciphertexts = {{ {{0}} }};
|
||||
|
||||
// Random seed
|
||||
uint256 randomSeed;
|
||||
@@ -71,19 +227,21 @@ public:
|
||||
// MACs
|
||||
// The verification of the JoinSplit requires these MACs
|
||||
// to be provided as an input.
|
||||
boost::array<uint256, ZC_NUM_JS_INPUTS> macs;
|
||||
std::array<uint256, ZC_NUM_JS_INPUTS> macs;
|
||||
|
||||
// JoinSplit proof
|
||||
// This is a zk-SNARK which ensures that this JoinSplit is valid.
|
||||
libzcash::ZCProof proof;
|
||||
libzcash::SproutProof proof;
|
||||
|
||||
JSDescription(): vpub_old(0), vpub_new(0) { }
|
||||
|
||||
JSDescription(ZCJoinSplit& params,
|
||||
const uint256& pubKeyHash,
|
||||
JSDescription(
|
||||
bool makeGrothProof,
|
||||
ZCJoinSplit& params,
|
||||
const uint256& joinSplitPubKey,
|
||||
const uint256& rt,
|
||||
const boost::array<libzcash::JSInput, ZC_NUM_JS_INPUTS>& inputs,
|
||||
const boost::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS>& outputs,
|
||||
const std::array<libzcash::JSInput, ZC_NUM_JS_INPUTS>& inputs,
|
||||
const std::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS>& outputs,
|
||||
CAmount vpub_old,
|
||||
CAmount vpub_new,
|
||||
bool computeProof = true, // Set to false in some tests
|
||||
@@ -91,18 +249,14 @@ public:
|
||||
);
|
||||
|
||||
static JSDescription Randomized(
|
||||
bool makeGrothProof,
|
||||
ZCJoinSplit& params,
|
||||
const uint256& pubKeyHash,
|
||||
const uint256& joinSplitPubKey,
|
||||
const uint256& rt,
|
||||
boost::array<libzcash::JSInput, ZC_NUM_JS_INPUTS>& inputs,
|
||||
boost::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS>& outputs,
|
||||
#ifdef __LP64__
|
||||
boost::array<uint64_t, ZC_NUM_JS_INPUTS>& inputMap,
|
||||
boost::array<uint64_t, ZC_NUM_JS_OUTPUTS>& outputMap,
|
||||
#else
|
||||
boost::array<size_t, ZC_NUM_JS_INPUTS>& inputMap,
|
||||
boost::array<size_t, ZC_NUM_JS_OUTPUTS>& outputMap,
|
||||
#endif
|
||||
std::array<libzcash::JSInput, ZC_NUM_JS_INPUTS>& inputs,
|
||||
std::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS>& outputs,
|
||||
std::array<size_t, ZC_NUM_JS_INPUTS>& inputMap,
|
||||
std::array<size_t, ZC_NUM_JS_OUTPUTS>& outputMap,
|
||||
CAmount vpub_old,
|
||||
CAmount vpub_new,
|
||||
bool computeProof = true, // Set to false in some tests
|
||||
@@ -114,16 +268,22 @@ public:
|
||||
bool Verify(
|
||||
ZCJoinSplit& params,
|
||||
libzcash::ProofVerifier& verifier,
|
||||
const uint256& pubKeyHash
|
||||
const uint256& joinSplitPubKey
|
||||
) const;
|
||||
|
||||
// Returns the calculated h_sig
|
||||
uint256 h_sig(ZCJoinSplit& params, const uint256& pubKeyHash) const;
|
||||
uint256 h_sig(ZCJoinSplit& params, const uint256& joinSplitPubKey) const;
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
// nVersion is set by CTransaction and CMutableTransaction to
|
||||
// (tx.fOverwintered << 31) | tx.nVersion
|
||||
bool fOverwintered = s.GetVersion() >> 31;
|
||||
int32_t txVersion = s.GetVersion() & 0x7FFFFFFF;
|
||||
bool useGroth = fOverwintered && txVersion >= SAPLING_TX_VERSION;
|
||||
|
||||
READWRITE(vpub_old);
|
||||
READWRITE(vpub_new);
|
||||
READWRITE(anchor);
|
||||
@@ -132,7 +292,7 @@ public:
|
||||
READWRITE(ephemeralKey);
|
||||
READWRITE(randomSeed);
|
||||
READWRITE(macs);
|
||||
READWRITE(proof);
|
||||
::SerReadWriteSproutProof(s, proof, useGroth, ser_action);
|
||||
READWRITE(ciphertexts);
|
||||
}
|
||||
|
||||
@@ -158,20 +318,19 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/** An outpoint - a combination of a transaction hash and an index n into its vout */
|
||||
class COutPoint
|
||||
class BaseOutPoint
|
||||
{
|
||||
public:
|
||||
uint256 hash;
|
||||
uint32_t n;
|
||||
|
||||
COutPoint() { SetNull(); }
|
||||
COutPoint(uint256 hashIn, uint32_t nIn) { hash = hashIn; n = nIn; }
|
||||
BaseOutPoint() { SetNull(); }
|
||||
BaseOutPoint(uint256 hashIn, uint32_t nIn) { hash = hashIn; n = nIn; }
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(hash);
|
||||
READWRITE(n);
|
||||
}
|
||||
@@ -179,21 +338,38 @@ public:
|
||||
void SetNull() { hash.SetNull(); n = (uint32_t) -1; }
|
||||
bool IsNull() const { return (hash.IsNull() && n == (uint32_t) -1); }
|
||||
|
||||
friend bool operator<(const COutPoint& a, const COutPoint& b)
|
||||
friend bool operator<(const BaseOutPoint& a, const BaseOutPoint& b)
|
||||
{
|
||||
return (a.hash < b.hash || (a.hash == b.hash && a.n < b.n));
|
||||
}
|
||||
|
||||
friend bool operator==(const COutPoint& a, const COutPoint& b)
|
||||
friend bool operator==(const BaseOutPoint& a, const BaseOutPoint& b)
|
||||
{
|
||||
return (a.hash == b.hash && a.n == b.n);
|
||||
}
|
||||
|
||||
friend bool operator!=(const COutPoint& a, const COutPoint& b)
|
||||
friend bool operator!=(const BaseOutPoint& a, const BaseOutPoint& b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
};
|
||||
|
||||
/** An outpoint - a combination of a transaction hash and an index n into its vout */
|
||||
class COutPoint : public BaseOutPoint
|
||||
{
|
||||
public:
|
||||
COutPoint() : BaseOutPoint() {};
|
||||
COutPoint(uint256 hashIn, uint32_t nIn) : BaseOutPoint(hashIn, nIn) {};
|
||||
std::string ToString() const;
|
||||
};
|
||||
|
||||
/** An outpoint - a combination of a transaction hash and an index n into its sapling
|
||||
* output description (vShieldedOutput) */
|
||||
class SaplingOutPoint : public BaseOutPoint
|
||||
{
|
||||
public:
|
||||
SaplingOutPoint() : BaseOutPoint() {};
|
||||
SaplingOutPoint(uint256 hashIn, uint32_t nIn) : BaseOutPoint(hashIn, nIn) {};
|
||||
std::string ToString() const;
|
||||
};
|
||||
|
||||
@@ -219,9 +395,9 @@ public:
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(prevout);
|
||||
READWRITE(scriptSig);
|
||||
READWRITE(*(CScriptBase*)(&scriptSig));
|
||||
READWRITE(nSequence);
|
||||
}
|
||||
|
||||
@@ -264,9 +440,9 @@ public:
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(nValue);
|
||||
READWRITE(scriptPubKey);
|
||||
READWRITE(*(CScriptBase*)(&scriptPubKey));
|
||||
}
|
||||
|
||||
void SetNull()
|
||||
@@ -295,7 +471,7 @@ public:
|
||||
if (scriptPubKey.IsUnspendable())
|
||||
return 0;
|
||||
|
||||
size_t nSize = GetSerializeSize(SER_DISK,0)+148u;
|
||||
size_t nSize = GetSerializeSize(*this, SER_DISK, 0) + 148u;
|
||||
return 3*minRelayTxFee.GetFee(nSize);
|
||||
}
|
||||
|
||||
@@ -321,6 +497,10 @@ public:
|
||||
static constexpr uint32_t OVERWINTER_VERSION_GROUP_ID = 0x03C48270;
|
||||
static_assert(OVERWINTER_VERSION_GROUP_ID != 0, "version group id must be non-zero as specified in ZIP 202");
|
||||
|
||||
// Sapling version group id
|
||||
static constexpr uint32_t SAPLING_VERSION_GROUP_ID = 0x892F2085;
|
||||
static_assert(SAPLING_VERSION_GROUP_ID != 0, "version group id must be non-zero as specified in ZIP 202");
|
||||
|
||||
struct CMutableTransaction;
|
||||
|
||||
/** The basic transaction that is broadcasted on the network and contained in
|
||||
@@ -340,13 +520,16 @@ protected:
|
||||
CTransaction(const CMutableTransaction &tx, bool evilDeveloperFlag);
|
||||
|
||||
public:
|
||||
typedef boost::array<unsigned char, 64> joinsplit_sig_t;
|
||||
typedef std::array<unsigned char, 64> joinsplit_sig_t;
|
||||
typedef std::array<unsigned char, 64> binding_sig_t;
|
||||
|
||||
// Transactions that include a list of JoinSplits are >= version 2.
|
||||
static const int32_t SPROUT_MIN_CURRENT_VERSION = 1;
|
||||
static const int32_t SPROUT_MAX_CURRENT_VERSION = 2;
|
||||
static const int32_t OVERWINTER_MIN_CURRENT_VERSION = 3;
|
||||
static const int32_t OVERWINTER_MAX_CURRENT_VERSION = 3;
|
||||
static const int32_t SAPLING_MIN_CURRENT_VERSION = 4;
|
||||
static const int32_t SAPLING_MAX_CURRENT_VERSION = 4;
|
||||
|
||||
static_assert(SPROUT_MIN_CURRENT_VERSION >= SPROUT_MIN_TX_VERSION,
|
||||
"standard rule for tx version should be consistent with network rule");
|
||||
@@ -358,6 +541,13 @@ public:
|
||||
OVERWINTER_MAX_CURRENT_VERSION >= OVERWINTER_MIN_CURRENT_VERSION),
|
||||
"standard rule for tx version should be consistent with network rule");
|
||||
|
||||
static_assert(SAPLING_MIN_CURRENT_VERSION >= SAPLING_MIN_TX_VERSION,
|
||||
"standard rule for tx version should be consistent with network rule");
|
||||
|
||||
static_assert( (SAPLING_MAX_CURRENT_VERSION <= SAPLING_MAX_TX_VERSION &&
|
||||
SAPLING_MAX_CURRENT_VERSION >= SAPLING_MIN_CURRENT_VERSION),
|
||||
"standard rule for tx version should be consistent with network rule");
|
||||
|
||||
// The local variables are made const to prevent unintended modification
|
||||
// without updating the cached hash value. However, CTransaction is not
|
||||
// actually immutable; deserialization and assignment are implemented,
|
||||
@@ -370,61 +560,82 @@ public:
|
||||
const std::vector<CTxOut> vout;
|
||||
const uint32_t nLockTime;
|
||||
const uint32_t nExpiryHeight;
|
||||
const CAmount valueBalance;
|
||||
const std::vector<SpendDescription> vShieldedSpend;
|
||||
const std::vector<OutputDescription> vShieldedOutput;
|
||||
const std::vector<JSDescription> vjoinsplit;
|
||||
const uint256 joinSplitPubKey;
|
||||
const joinsplit_sig_t joinSplitSig = {{0}};
|
||||
const binding_sig_t bindingSig = {{0}};
|
||||
|
||||
/** Construct a CTransaction that qualifies as IsNull() */
|
||||
CTransaction();
|
||||
|
||||
/** Convert a CMutableTransaction into a CTransaction. */
|
||||
CTransaction(const CMutableTransaction &tx);
|
||||
CTransaction(CMutableTransaction &&tx);
|
||||
|
||||
CTransaction& operator=(const CTransaction& tx);
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
uint32_t header;
|
||||
if (ser_action.ForRead()) {
|
||||
// When deserializing, unpack the 4 byte header to extract fOverwintered and nVersion.
|
||||
uint32_t header;
|
||||
READWRITE(header);
|
||||
*const_cast<bool*>(&fOverwintered) = header >> 31;
|
||||
*const_cast<int32_t*>(&this->nVersion) = header & 0x7FFFFFFF;
|
||||
} else {
|
||||
uint32_t header = GetHeader();
|
||||
header = GetHeader();
|
||||
READWRITE(header);
|
||||
}
|
||||
nVersion = this->nVersion;
|
||||
if (fOverwintered) {
|
||||
READWRITE(*const_cast<uint32_t*>(&this->nVersionGroupId));
|
||||
}
|
||||
|
||||
bool isOverwinterV3 = fOverwintered &&
|
||||
nVersionGroupId == OVERWINTER_VERSION_GROUP_ID &&
|
||||
nVersion == 3;
|
||||
if (fOverwintered && !isOverwinterV3) {
|
||||
bool isOverwinterV3 =
|
||||
fOverwintered &&
|
||||
nVersionGroupId == OVERWINTER_VERSION_GROUP_ID &&
|
||||
nVersion == OVERWINTER_TX_VERSION;
|
||||
bool isSaplingV4 =
|
||||
fOverwintered &&
|
||||
nVersionGroupId == SAPLING_VERSION_GROUP_ID &&
|
||||
nVersion == SAPLING_TX_VERSION;
|
||||
if (fOverwintered && !(isOverwinterV3 || isSaplingV4)) {
|
||||
throw std::ios_base::failure("Unknown transaction format");
|
||||
}
|
||||
|
||||
READWRITE(*const_cast<std::vector<CTxIn>*>(&vin));
|
||||
READWRITE(*const_cast<std::vector<CTxOut>*>(&vout));
|
||||
READWRITE(*const_cast<uint32_t*>(&nLockTime));
|
||||
if (isOverwinterV3) {
|
||||
if (isOverwinterV3 || isSaplingV4) {
|
||||
READWRITE(*const_cast<uint32_t*>(&nExpiryHeight));
|
||||
}
|
||||
if (isSaplingV4) {
|
||||
READWRITE(*const_cast<CAmount*>(&valueBalance));
|
||||
READWRITE(*const_cast<std::vector<SpendDescription>*>(&vShieldedSpend));
|
||||
READWRITE(*const_cast<std::vector<OutputDescription>*>(&vShieldedOutput));
|
||||
}
|
||||
if (nVersion >= 2) {
|
||||
READWRITE(*const_cast<std::vector<JSDescription>*>(&vjoinsplit));
|
||||
auto os = WithVersion(&s, static_cast<int>(header));
|
||||
::SerReadWrite(os, *const_cast<std::vector<JSDescription>*>(&vjoinsplit), ser_action);
|
||||
if (vjoinsplit.size() > 0) {
|
||||
READWRITE(*const_cast<uint256*>(&joinSplitPubKey));
|
||||
READWRITE(*const_cast<joinsplit_sig_t*>(&joinSplitSig));
|
||||
}
|
||||
}
|
||||
if (isSaplingV4 && !(vShieldedSpend.empty() && vShieldedOutput.empty())) {
|
||||
READWRITE(*const_cast<binding_sig_t*>(&bindingSig));
|
||||
}
|
||||
if (ser_action.ForRead())
|
||||
UpdateHash();
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
CTransaction(deserialize_type, Stream& s) : CTransaction(CMutableTransaction(deserialize, s)) {}
|
||||
|
||||
bool IsNull() const {
|
||||
return vin.empty() && vout.empty();
|
||||
}
|
||||
@@ -443,15 +654,24 @@ public:
|
||||
return header;
|
||||
}
|
||||
|
||||
// Return sum of txouts.
|
||||
/*
|
||||
* Context for the two methods below:
|
||||
* As at most one of vpub_new and vpub_old is non-zero in every JoinSplit,
|
||||
* we can think of a JoinSplit as an input or output according to which one
|
||||
* it is (e.g. if vpub_new is non-zero the joinSplit is "giving value" to
|
||||
* the outputs in the transaction). Similarly, we can think of the Sapling
|
||||
* shielded part of the transaction as an input or output according to
|
||||
* whether valueBalance - the sum of shielded input values minus the sum of
|
||||
* shielded output values - is positive or negative.
|
||||
*/
|
||||
|
||||
// Return sum of txouts, (negative valueBalance or zero) and JoinSplit vpub_old.
|
||||
CAmount GetValueOut() const;
|
||||
// GetValueIn() is a method on CCoinsViewCache, because
|
||||
// inputs must be known to compute value in.
|
||||
|
||||
// Return sum of JoinSplit vpub_new
|
||||
CAmount GetJoinSplitValueIn() const;
|
||||
// Return sum of JoinSplit vpub_old
|
||||
CAmount GetJoinSplitValueOut() const;
|
||||
// Return sum of (positive valueBalance or zero) and JoinSplit vpub_new
|
||||
CAmount GetShieldedValueIn() const;
|
||||
|
||||
// Compute priority, given priority of inputs and (optionally) tx size
|
||||
double ComputePriority(double dPriorityInputs, unsigned int nTxSize=0) const;
|
||||
@@ -535,9 +755,13 @@ struct CMutableTransaction
|
||||
std::vector<CTxOut> vout;
|
||||
uint32_t nLockTime;
|
||||
uint32_t nExpiryHeight;
|
||||
CAmount valueBalance;
|
||||
std::vector<SpendDescription> vShieldedSpend;
|
||||
std::vector<OutputDescription> vShieldedOutput;
|
||||
std::vector<JSDescription> vjoinsplit;
|
||||
uint256 joinSplitPubKey;
|
||||
CTransaction::joinsplit_sig_t joinSplitSig = {{0}};
|
||||
CTransaction::binding_sig_t bindingSig = {{0}};
|
||||
|
||||
CMutableTransaction();
|
||||
CMutableTransaction(const CTransaction& tx);
|
||||
@@ -545,47 +769,65 @@ struct CMutableTransaction
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
uint32_t header;
|
||||
if (ser_action.ForRead()) {
|
||||
// When deserializing, unpack the 4 byte header to extract fOverwintered and nVersion.
|
||||
uint32_t header;
|
||||
READWRITE(header);
|
||||
fOverwintered = header >> 31;
|
||||
this->nVersion = header & 0x7FFFFFFF;
|
||||
} else {
|
||||
// When serializing v1 and v2, the 4 byte header is nVersion
|
||||
uint32_t header = this->nVersion;
|
||||
header = this->nVersion;
|
||||
// When serializing Overwintered tx, the 4 byte header is the combination of fOverwintered and nVersion
|
||||
if (fOverwintered) {
|
||||
header |= 1 << 31;
|
||||
}
|
||||
READWRITE(header);
|
||||
}
|
||||
nVersion = this->nVersion;
|
||||
if (fOverwintered) {
|
||||
READWRITE(nVersionGroupId);
|
||||
}
|
||||
|
||||
bool isOverwinterV3 = fOverwintered &&
|
||||
nVersionGroupId == OVERWINTER_VERSION_GROUP_ID &&
|
||||
nVersion == 3;
|
||||
if (fOverwintered && !isOverwinterV3) {
|
||||
bool isOverwinterV3 =
|
||||
fOverwintered &&
|
||||
nVersionGroupId == OVERWINTER_VERSION_GROUP_ID &&
|
||||
nVersion == OVERWINTER_TX_VERSION;
|
||||
bool isSaplingV4 =
|
||||
fOverwintered &&
|
||||
nVersionGroupId == SAPLING_VERSION_GROUP_ID &&
|
||||
nVersion == SAPLING_TX_VERSION;
|
||||
if (fOverwintered && !(isOverwinterV3 || isSaplingV4)) {
|
||||
throw std::ios_base::failure("Unknown transaction format");
|
||||
}
|
||||
|
||||
READWRITE(vin);
|
||||
READWRITE(vout);
|
||||
READWRITE(nLockTime);
|
||||
if (isOverwinterV3) {
|
||||
if (isOverwinterV3 || isSaplingV4) {
|
||||
READWRITE(nExpiryHeight);
|
||||
}
|
||||
if (isSaplingV4) {
|
||||
READWRITE(valueBalance);
|
||||
READWRITE(vShieldedSpend);
|
||||
READWRITE(vShieldedOutput);
|
||||
}
|
||||
if (nVersion >= 2) {
|
||||
READWRITE(vjoinsplit);
|
||||
auto os = WithVersion(&s, static_cast<int>(header));
|
||||
::SerReadWrite(os, vjoinsplit, ser_action);
|
||||
if (vjoinsplit.size() > 0) {
|
||||
READWRITE(joinSplitPubKey);
|
||||
READWRITE(joinSplitSig);
|
||||
}
|
||||
}
|
||||
if (isSaplingV4 && !(vShieldedSpend.empty() && vShieldedOutput.empty())) {
|
||||
READWRITE(bindingSig);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
CMutableTransaction(deserialize_type, Stream& s) {
|
||||
Unserialize(s);
|
||||
}
|
||||
|
||||
/** Compute the hash of this CMutableTransaction. This is computed on the
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user