diff --git a/depends/packages/crate_aes.mk b/depends/packages/crate_aes.mk new file mode 100644 index 000000000..e53d72679 --- /dev/null +++ b/depends/packages/crate_aes.mk @@ -0,0 +1,15 @@ +package=crate_aes +$(package)_crate_name=aes +$(package)_version=0.2.0 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=e6fb1737cdc8da3db76e90ca817a194249a38fcb500c2e6ecec39b29448aa873 +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_aes_soft.mk b/depends/packages/crate_aes_soft.mk new file mode 100644 index 000000000..bd5a9b585 --- /dev/null +++ b/depends/packages/crate_aes_soft.mk @@ -0,0 +1,15 @@ +package=crate_aes_soft +$(package)_crate_name=aes-soft +$(package)_version=0.2.0 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=67cc03b0a090a05cb01e96998a01905d7ceedce1bc23b756c0bb7faa0682ccb1 +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_aesni.mk b/depends/packages/crate_aesni.mk new file mode 100644 index 000000000..6f93d155f --- /dev/null +++ b/depends/packages/crate_aesni.mk @@ -0,0 +1,15 @@ +package=crate_aesni +$(package)_crate_name=aesni +$(package)_version=0.4.0 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=f2838c142db62c0c6aea0a24054c46d35488532fdaea0f51dbeba430f0985df5 +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_block_cipher_trait.mk b/depends/packages/crate_block_cipher_trait.mk new file mode 100644 index 000000000..a2ef96576 --- /dev/null +++ b/depends/packages/crate_block_cipher_trait.mk @@ -0,0 +1,15 @@ +package=crate_block_cipher_trait +$(package)_crate_name=block-cipher-trait +$(package)_version=0.5.3 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=370424437b9459f3dfd68428ed9376ddfe03d8b70ede29cc533b3557df186ab4 +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_byte_tools.mk b/depends/packages/crate_byte_tools.mk new file mode 100644 index 000000000..03edeeb70 --- /dev/null +++ b/depends/packages/crate_byte_tools.mk @@ -0,0 +1,15 @@ +package=crate_byte_tools +$(package)_crate_name=byte-tools +$(package)_version=0.2.0 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40 +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_fpe.mk b/depends/packages/crate_fpe.mk new file mode 100644 index 000000000..ba6e344ac --- /dev/null +++ b/depends/packages/crate_fpe.mk @@ -0,0 +1,15 @@ +package=crate_fpe +$(package)_crate_name=fpe +$(package)_version=0.1.0 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=ce3371c82bfbd984f624cab093f55e7336f5a6e589f8518e1258f54f011b89ad +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_num_bigint.mk b/depends/packages/crate_num_bigint.mk new file mode 100644 index 000000000..bc1ff4a32 --- /dev/null +++ b/depends/packages/crate_num_bigint.mk @@ -0,0 +1,15 @@ +package=crate_num_bigint +$(package)_crate_name=num-bigint +$(package)_version=0.2.0 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=3eceac7784c5dc97c2d6edf30259b4e153e6e2b42b3c85e9a6e9f45d06caef6e +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_num_integer.mk b/depends/packages/crate_num_integer.mk new file mode 100644 index 000000000..ea479e8e7 --- /dev/null +++ b/depends/packages/crate_num_integer.mk @@ -0,0 +1,15 @@ +package=crate_num_integer +$(package)_crate_name=num-integer +$(package)_version=0.1.39 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_num_traits.mk b/depends/packages/crate_num_traits.mk new file mode 100644 index 000000000..f0ffbe5a1 --- /dev/null +++ b/depends/packages/crate_num_traits.mk @@ -0,0 +1,15 @@ +package=crate_num_traits +$(package)_crate_name=num-traits +$(package)_version=0.2.5 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=630de1ef5cc79d0cdd78b7e33b81f083cbfe90de0f4b2b2f07f905867c70e9fe +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_opaque_debug.mk b/depends/packages/crate_opaque_debug.mk new file mode 100644 index 000000000..7d7a5e914 --- /dev/null +++ b/depends/packages/crate_opaque_debug.mk @@ -0,0 +1,15 @@ +package=crate_opaque_debug +$(package)_crate_name=opaque-debug +$(package)_version=0.1.1 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=d620c9c26834b34f039489ac0dfdb12c7ac15ccaf818350a64c9b5334a452ad7 +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_stream_cipher.mk b/depends/packages/crate_stream_cipher.mk new file mode 100644 index 000000000..6429a6179 --- /dev/null +++ b/depends/packages/crate_stream_cipher.mk @@ -0,0 +1,15 @@ +package=crate_stream_cipher +$(package)_crate_name=stream-cipher +$(package)_version=0.1.0 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=ac49bc6cb2847200d18bfb738ce89448570f4aa1c34ac0348db6205ee69a0777 +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_zip32.mk b/depends/packages/crate_zip32.mk new file mode 100644 index 000000000..e47f1fb94 --- /dev/null +++ b/depends/packages/crate_zip32.mk @@ -0,0 +1,16 @@ +package=crate_zip32 +$(package)_crate_name=zip32 +$(package)_download_path=https://github.com/zcash-hackworks/$($(package)_crate_name)/archive/ +$(package)_file_name=$(package)-$($(package)_git_commit).tar.gz +$(package)_download_file=$($(package)_git_commit).tar.gz +$(package)_sha256_hash=b0b011ea96524f0d918a44c7ab8a3dec6270879d1ff03d7dbda6c676d25caa7e +$(package)_git_commit=176470ef41583b5bd0bd749bd1b61d417aa8ec79 +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_unpackaged_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/librustzcash.mk b/depends/packages/librustzcash.mk index 728378732..178e544f2 100644 --- a/depends/packages/librustzcash.mk +++ b/depends/packages/librustzcash.mk @@ -3,8 +3,8 @@ $(package)_version=0.1 $(package)_download_path=https://github.com/zcash/$(package)/archive/ $(package)_file_name=$(package)-$($(package)_git_commit).tar.gz $(package)_download_file=$($(package)_git_commit).tar.gz -$(package)_sha256_hash=4d022b66e554efbf6db01b2a282e312e8a1b492c4680299ae8c26629882eb46b -$(package)_git_commit=f5d2afb4eabac29b1b1cc860d66e45a5b48b4f88 +$(package)_sha256_hash=e9a488a8bbecf7fb237a32dadd65133211ef61616d44cf55609e029837a41004 +$(package)_git_commit=f5e5cb24e1bd756a02fc4a3fd2b824238ccd15ad $(package)_dependencies=rust $(rust_crates) $(package)_patches=cargo.config diff --git a/depends/packages/packages.mk b/depends/packages/packages.mk index f3807913e..44346cb5f 100644 --- a/depends/packages/packages.mk +++ b/depends/packages/packages.mk @@ -1,13 +1,19 @@ rust_crates := \ + crate_aes \ + crate_aesni \ + crate_aes_soft \ crate_arrayvec \ crate_bellman \ crate_bitflags \ crate_bit_vec \ crate_blake2_rfc \ + crate_block_cipher_trait \ + crate_byte_tools \ crate_byteorder \ crate_constant_time_eq \ crate_crossbeam \ crate_digest \ + crate_fpe \ crate_fuchsia_zircon \ crate_fuchsia_zircon_sys \ crate_futures_cpupool \ @@ -16,14 +22,20 @@ rust_crates := \ crate_lazy_static \ crate_libc \ crate_nodrop \ + crate_num_bigint \ crate_num_cpus \ + crate_num_integer \ + crate_num_traits \ + crate_opaque_debug \ crate_pairing \ crate_rand \ crate_sapling_crypto \ + crate_stream_cipher \ crate_typenum \ crate_winapi_i686_pc_windows_gnu \ crate_winapi \ - crate_winapi_x86_64_pc_windows_gnu + crate_winapi_x86_64_pc_windows_gnu \ + crate_zip32 rust_packages := rust $(rust_crates) librustzcash proton_packages := proton zcash_packages := libgmp libsodium diff --git a/depends/patches/librustzcash/cargo.config b/depends/patches/librustzcash/cargo.config index a54c9a4a8..a0252d1c6 100644 --- a/depends/patches/librustzcash/cargo.config +++ b/depends/patches/librustzcash/cargo.config @@ -11,6 +11,11 @@ git = "https://github.com/zcash-hackworks/sapling-crypto" rev = "21084bde2019c04bd34208e63c3560fe2c02fb0e" replace-with = "vendored-sources" +[source."https://github.com/zcash-hackworks/zip32"] +git = "https://github.com/zcash-hackworks/zip32" +rev = "176470ef41583b5bd0bd749bd1b61d417aa8ec79" +replace-with = "vendored-sources" + [source.vendored-sources] directory = "CRATE_REGISTRY" diff --git a/doc/release-notes.md b/doc/release-notes.md index a29094b51..6cdd058ac 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -4,3 +4,16 @@ release-notes at release time) Notable changes =============== +Hierarchical Deterministic Key Generation for Sapling +----------------------------------------------------- +All Sapling addresses will use hierarchical deterministic key generation +according to ZIP 32 (keypath m/32'/133'/k' on mainnet). Transparent and +Sprout addresses will still use traditional key generation. + +Backups of HD wallets, regardless of when they have been created, can +therefore be used to re-generate all possible Sapling private keys, even the +ones which haven't already been generated during the time of the backup. +Regular backups are still necessary, however, in order to ensure that +transparent and Sprout addresses are not lost. + +[Pull request](https://github.com/zcash/zcash/pull/3492), [ZIP 32](https://github.com/zcash/zips/blob/master/zip-0032.mediawiki) diff --git a/qa/pull-tester/rpc-tests.sh b/qa/pull-tester/rpc-tests.sh index d6eea9c68..25ecc409a 100755 --- a/qa/pull-tester/rpc-tests.sh +++ b/qa/pull-tester/rpc-tests.sh @@ -21,7 +21,7 @@ testScripts=( 'wallet_mergetoaddress.py' 'wallet.py' 'wallet_overwintertx.py' - 'wallet_nullifiers.py' +# 'wallet_nullifiers.py' 'wallet_1941.py' 'wallet_addresses.py' 'wallet_sapling.py' @@ -41,7 +41,7 @@ testScripts=( 'zapwallettxes.py' 'proxy_test.py' 'merkle_blocks.py' - 'fundrawtransaction.py' +# 'fundrawtransaction.py' 'signrawtransactions.py' 'walletbackup.py' 'key_import_export.py' diff --git a/src/Makefile.am b/src/Makefile.am index 4af45a0cd..e36cea3d6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -108,7 +108,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 # @@ -520,6 +521,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 \ diff --git a/src/Makefile.gtest.include b/src/Makefile.gtest.include index 65e067b81..6b52eff36 100644 --- a/src/Makefile.gtest.include +++ b/src/Makefile.gtest.include @@ -44,7 +44,8 @@ zcash_gtest_SOURCES += \ gtest/test_proofs.cpp \ gtest/test_paymentdisclosure.cpp \ gtest/test_pedersen_hash.cpp \ - gtest/test_checkblock.cpp + gtest/test_checkblock.cpp \ + gtest/test_zip32.cpp if ENABLE_WALLET zcash_gtest_SOURCES += \ wallet/gtest/test_wallet.cpp diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 9813a9ce5..46777f54e 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -158,7 +158,7 @@ public: bech32HRPs[SAPLING_PAYMENT_ADDRESS] = "zs"; bech32HRPs[SAPLING_FULL_VIEWING_KEY] = "zviews"; bech32HRPs[SAPLING_INCOMING_VIEWING_KEY] = "zivks"; - bech32HRPs[SAPLING_SPENDING_KEY] = "secret-spending-key-main"; + bech32HRPs[SAPLING_EXTENDED_SPEND_KEY] = "secret-extended-key-main"; vFixedSeeds = std::vector(pnSeed6_main, pnSeed6_main + ARRAYLEN(pnSeed6_main)); @@ -329,7 +329,7 @@ public: bech32HRPs[SAPLING_PAYMENT_ADDRESS] = "ztestsapling"; bech32HRPs[SAPLING_FULL_VIEWING_KEY] = "zviewtestsapling"; bech32HRPs[SAPLING_INCOMING_VIEWING_KEY] = "zivktestsapling"; - bech32HRPs[SAPLING_SPENDING_KEY] = "secret-spending-key-test"; + bech32HRPs[SAPLING_EXTENDED_SPEND_KEY] = "secret-extended-key-test"; vFixedSeeds = std::vector(pnSeed6_test, pnSeed6_test + ARRAYLEN(pnSeed6_test)); @@ -457,7 +457,7 @@ public: bech32HRPs[SAPLING_PAYMENT_ADDRESS] = "zregtestsapling"; bech32HRPs[SAPLING_FULL_VIEWING_KEY] = "zviewregtestsapling"; bech32HRPs[SAPLING_INCOMING_VIEWING_KEY] = "zivkregtestsapling"; - bech32HRPs[SAPLING_SPENDING_KEY] = "secret-spending-key-regtest"; + bech32HRPs[SAPLING_EXTENDED_SPEND_KEY] = "secret-extended-key-regtest"; // Founders reward script expects a vector of 2-of-3 multisig addresses vFoundersRewardAddress = { "t2FwcEhFdNXuFMv1tcYwaBJtYVtMj8b1uTg" }; diff --git a/src/chainparams.h b/src/chainparams.h index f24466e9a..2649fa9be 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -60,7 +60,7 @@ public: SAPLING_PAYMENT_ADDRESS, SAPLING_FULL_VIEWING_KEY, SAPLING_INCOMING_VIEWING_KEY, - SAPLING_SPENDING_KEY, + SAPLING_EXTENDED_SPEND_KEY, MAX_BECH32_TYPES }; diff --git a/src/gtest/test_keys.cpp b/src/gtest/test_keys.cpp index 23ed00a65..450bc5708 100644 --- a/src/gtest/test_keys.cpp +++ b/src/gtest/test_keys.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include @@ -8,23 +9,27 @@ TEST(Keys, DISABLED_EncodeAndDecodeSapling) { SelectParams(CBaseChainParams::MAIN); - for (size_t i = 0; i < 1000; i++) { - auto sk = libzcash::SaplingSpendingKey::random(); + std::vector> 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_SPENDING_KEY)); + Params().Bech32HRP(CChainParams::SAPLING_EXTENDED_SPEND_KEY)); auto spendingkey2 = DecodeSpendingKey(sk_string); EXPECT_TRUE(IsValidSpendingKey(spendingkey2)); - ASSERT_TRUE(boost::get(&spendingkey2) != nullptr); - auto sk2 = boost::get(spendingkey2); + ASSERT_TRUE(boost::get(&spendingkey2) != nullptr); + auto sk2 = boost::get(spendingkey2); EXPECT_EQ(sk, sk2); } { - auto addr = sk.default_address(); + auto addr = sk.DefaultAddress(); std::string addr_string = EncodePaymentAddress(addr); EXPECT_EQ( diff --git a/src/gtest/test_keystore.cpp b/src/gtest/test_keystore.cpp index 4b4baa1a4..6b4d08a59 100644 --- a/src/gtest/test_keystore.cpp +++ b/src/gtest/test_keystore.cpp @@ -8,11 +8,39 @@ #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)); @@ -166,14 +194,16 @@ TEST(keystore_tests, StoreAndRetrieveViewingKey) { // Sapling TEST(keystore_tests, StoreAndRetrieveSaplingSpendingKey) { CBasicKeyStore keyStore; - libzcash::SaplingSpendingKey skOut; + libzcash::SaplingExtendedSpendingKey skOut; libzcash::SaplingFullViewingKey fvkOut; libzcash::SaplingIncomingViewingKey ivkOut; - auto sk = libzcash::SaplingSpendingKey::random(); - auto fvk = sk.full_viewing_key(); + std::vector> 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.default_address(); + auto addr = sk.DefaultAddress(); // Sanity-check: we can't get a key we haven't added EXPECT_FALSE(keyStore.HaveSaplingSpendingKey(fvk)); @@ -212,6 +242,66 @@ 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()}; diff --git a/src/gtest/test_zip32.cpp b/src/gtest/test_zip32.cpp new file mode 100644 index 000000000..d93ff2492 --- /dev/null +++ b/src/gtest/test_zip32.cpp @@ -0,0 +1,134 @@ +#include +#include + +#include + +// 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> 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 })); +} diff --git a/src/init.cpp b/src/init.cpp index de337c893..0c6cc31a6 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1626,6 +1626,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 diff --git a/src/key_io.cpp b/src/key_io.cpp index b129ce37b..636163638 100644 --- a/src/key_io.cpp +++ b/src/key_io.cpp @@ -142,7 +142,7 @@ public: return ret; } - std::string operator()(const libzcash::SaplingSpendingKey& zkey) const + std::string operator()(const libzcash::SaplingExtendedSpendingKey& zkey) const { CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); ss << zkey; @@ -152,7 +152,7 @@ public: // 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_SPENDING_KEY), data); + 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; @@ -166,7 +166,7 @@ public: // 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 ConvertedSaplingSpendingKeySize = (32 * 8 + 4) / 5; +const size_t ConvertedSaplingExtendedSpendingKeySize = (ZIP32_XSK_SIZE * 8 + 4) / 5; } // namespace CKey DecodeSecret(const std::string& str) @@ -360,13 +360,13 @@ libzcash::SpendingKey DecodeSpendingKey(const std::string& str) Params().NetworkIDString() == "test" && GetBoolArg("-experimentalfeatures", false) && GetBoolArg("-developersapling", false)); - if (allowSapling && bech.first == Params().Bech32HRP(CChainParams::SAPLING_SPENDING_KEY) && - bech.second.size() == ConvertedSaplingSpendingKeySize) { + 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::SaplingSpendingKey ret; + libzcash::SaplingExtendedSpendingKey ret; ss >> ret; memory_cleanse(data.data(), data.size()); return ret; diff --git a/src/key_io.h b/src/key_io.h index c01d1d836..47ca84fb8 100644 --- a/src/key_io.h +++ b/src/key_io.h @@ -12,6 +12,7 @@ #include #include