From f8d65e8f03fad5d6627b87b52b642bdf9df92a92 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 3 Aug 2018 15:59:50 +0100 Subject: [PATCH 01/12] Update librustzcash with ZIP 32 APIs --- depends/packages/crate_aes.mk | 15 +++++++++++++++ depends/packages/crate_aes_soft.mk | 15 +++++++++++++++ depends/packages/crate_aesni.mk | 15 +++++++++++++++ depends/packages/crate_block_cipher_trait.mk | 15 +++++++++++++++ depends/packages/crate_byte_tools.mk | 15 +++++++++++++++ depends/packages/crate_fpe.mk | 15 +++++++++++++++ depends/packages/crate_num_bigint.mk | 15 +++++++++++++++ depends/packages/crate_num_integer.mk | 15 +++++++++++++++ depends/packages/crate_num_traits.mk | 15 +++++++++++++++ depends/packages/crate_opaque_debug.mk | 15 +++++++++++++++ depends/packages/crate_stream_cipher.mk | 15 +++++++++++++++ depends/packages/crate_zip32.mk | 16 ++++++++++++++++ depends/packages/librustzcash.mk | 4 ++-- depends/packages/packages.mk | 14 +++++++++++++- depends/patches/librustzcash/cargo.config | 5 +++++ 15 files changed, 201 insertions(+), 3 deletions(-) create mode 100644 depends/packages/crate_aes.mk create mode 100644 depends/packages/crate_aes_soft.mk create mode 100644 depends/packages/crate_aesni.mk create mode 100644 depends/packages/crate_block_cipher_trait.mk create mode 100644 depends/packages/crate_byte_tools.mk create mode 100644 depends/packages/crate_fpe.mk create mode 100644 depends/packages/crate_num_bigint.mk create mode 100644 depends/packages/crate_num_integer.mk create mode 100644 depends/packages/crate_num_traits.mk create mode 100644 depends/packages/crate_opaque_debug.mk create mode 100644 depends/packages/crate_stream_cipher.mk create mode 100644 depends/packages/crate_zip32.mk 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" From 7614198f9db0b4ef86a6678deaf24d5565920047 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 3 Aug 2018 16:04:28 +0100 Subject: [PATCH 02/12] ZIP 32 Sapling structs --- src/Makefile.am | 4 +- src/Makefile.gtest.include | 3 +- src/gtest/test_zip32.cpp | 134 +++++++++++++++++++++++++++++++++++ src/uint256.h | 9 +++ src/zcash/Address.hpp | 3 + src/zcash/zip32.cpp | 139 +++++++++++++++++++++++++++++++++++++ src/zcash/zip32.h | 121 ++++++++++++++++++++++++++++++++ 7 files changed, 411 insertions(+), 2 deletions(-) create mode 100644 src/gtest/test_zip32.cpp create mode 100644 src/zcash/zip32.cpp create mode 100644 src/zcash/zip32.h 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/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/uint256.h b/src/uint256.h index b3a6cb242..f22a8bafa 100644 --- a/src/uint256.h +++ b/src/uint256.h @@ -88,6 +88,15 @@ public: } }; +/** 88-bit opaque blob. + */ +class blob88 : public base_blob<88> { +public: + blob88() {} + blob88(const base_blob<88>& b) : base_blob<88>(b) {} + explicit blob88(const std::vector& vch) : base_blob<88>(vch) {} +}; + /** 160-bit opaque blob. * @note This type is called uint160 for historical reasons only. It is an opaque * blob of 160 bits and has no integer operations. diff --git a/src/zcash/Address.hpp b/src/zcash/Address.hpp index f73e585cf..00b8290d2 100644 --- a/src/zcash/Address.hpp +++ b/src/zcash/Address.hpp @@ -19,6 +19,9 @@ const size_t SerializedSproutPaymentAddressSize = 64; const size_t SerializedSproutViewingKeySize = 64; const size_t SerializedSproutSpendingKeySize = 32; +const size_t SerializedSaplingPaymentAddressSize = 43; +const size_t SerializedSaplingFullViewingKeySize = 96; +const size_t SerializedSaplingExpandedSpendingKeySize = 96; const size_t SerializedSaplingSpendingKeySize = 32; typedef std::array diversifier_t; diff --git a/src/zcash/zip32.cpp b/src/zcash/zip32.cpp new file mode 100644 index 000000000..43529525e --- /dev/null +++ b/src/zcash/zip32.cpp @@ -0,0 +1,139 @@ +// Copyright (c) 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 "zip32.h" + +#include "hash.h" +#include "random.h" +#include "streams.h" +#include "version.h" + +#include +#include + +const unsigned char ZCASH_HD_SEED_FP_PERSONAL[crypto_generichash_blake2b_PERSONALBYTES] = + {'Z', 'c', 'a', 's', 'h', '_', 'H', 'D', '_', 'S', 'e', 'e', 'd', '_', 'F', 'P'}; + +HDSeed HDSeed::Random(size_t len) +{ + assert(len >= 32); + RawHDSeed rawSeed(len, 0); + GetRandBytes(rawSeed.data(), len); + return HDSeed(rawSeed); +} + +uint256 HDSeed::Fingerprint() const +{ + CBLAKE2bWriter h(SER_GETHASH, 0, ZCASH_HD_SEED_FP_PERSONAL); + h << seed; + return h.GetHash(); +} + +namespace libzcash { + +boost::optional SaplingExtendedFullViewingKey::Derive(uint32_t i) const +{ + CDataStream ss_p(SER_NETWORK, PROTOCOL_VERSION); + ss_p << *this; + CSerializeData p_bytes(ss_p.begin(), ss_p.end()); + + CSerializeData i_bytes(ZIP32_XFVK_SIZE); + if (librustzcash_zip32_xfvk_derive( + reinterpret_cast(p_bytes.data()), + i, + reinterpret_cast(i_bytes.data()) + )) { + CDataStream ss_i(i_bytes, SER_NETWORK, PROTOCOL_VERSION); + SaplingExtendedFullViewingKey xfvk_i; + ss_i >> xfvk_i; + return xfvk_i; + } else { + return boost::none; + } +} + +boost::optional> + SaplingExtendedFullViewingKey::Address(diversifier_index_t j) const +{ + CDataStream ss_xfvk(SER_NETWORK, PROTOCOL_VERSION); + ss_xfvk << *this; + CSerializeData xfvk_bytes(ss_xfvk.begin(), ss_xfvk.end()); + + diversifier_index_t j_ret; + CSerializeData addr_bytes(libzcash::SerializedSaplingPaymentAddressSize); + if (librustzcash_zip32_xfvk_address( + reinterpret_cast(xfvk_bytes.data()), + j.begin(), j_ret.begin(), + reinterpret_cast(addr_bytes.data()))) { + CDataStream ss_addr(addr_bytes, SER_NETWORK, PROTOCOL_VERSION); + libzcash::SaplingPaymentAddress addr; + ss_addr >> addr; + return std::make_pair(j_ret, addr); + } else { + return boost::none; + } +} + +libzcash::SaplingPaymentAddress SaplingExtendedFullViewingKey::DefaultAddress() const +{ + diversifier_index_t j0; + auto addr = Address(j0); + // If we can't obtain a default address, we are *very* unlucky... + if (!addr) { + throw std::runtime_error("SaplingExtendedFullViewingKey::DefaultAddress(): No valid diversifiers out of 2^88!"); + } + return addr.get().second; +} + +SaplingExtendedSpendingKey SaplingExtendedSpendingKey::Master(const HDSeed& seed) +{ + auto rawSeed = seed.RawSeed(); + CSerializeData m_bytes(ZIP32_XSK_SIZE); + librustzcash_zip32_xsk_master( + rawSeed.data(), + rawSeed.size(), + reinterpret_cast(m_bytes.data())); + + CDataStream ss(m_bytes, SER_NETWORK, PROTOCOL_VERSION); + SaplingExtendedSpendingKey xsk_m; + ss >> xsk_m; + return xsk_m; +} + +SaplingExtendedSpendingKey SaplingExtendedSpendingKey::Derive(uint32_t i) const +{ + CDataStream ss_p(SER_NETWORK, PROTOCOL_VERSION); + ss_p << *this; + CSerializeData p_bytes(ss_p.begin(), ss_p.end()); + + CSerializeData i_bytes(ZIP32_XSK_SIZE); + librustzcash_zip32_xsk_derive( + reinterpret_cast(p_bytes.data()), + i, + reinterpret_cast(i_bytes.data())); + + CDataStream ss_i(i_bytes, SER_NETWORK, PROTOCOL_VERSION); + SaplingExtendedSpendingKey xsk_i; + ss_i >> xsk_i; + return xsk_i; +} + +SaplingExtendedFullViewingKey SaplingExtendedSpendingKey::ToXFVK() const +{ + SaplingExtendedFullViewingKey ret; + ret.depth = depth; + ret.parentFVKTag = parentFVKTag; + ret.childIndex = childIndex; + ret.chaincode = chaincode; + ret.fvk = expsk.full_viewing_key(); + ret.dk = dk; + return ret; +} + +libzcash::SaplingPaymentAddress SaplingExtendedSpendingKey::DefaultAddress() const +{ + return ToXFVK().DefaultAddress(); +} + +} diff --git a/src/zcash/zip32.h b/src/zcash/zip32.h new file mode 100644 index 000000000..9dce9e203 --- /dev/null +++ b/src/zcash/zip32.h @@ -0,0 +1,121 @@ +// Copyright (c) 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 ZCASH_ZIP32_H +#define ZCASH_ZIP32_H + +#include "serialize.h" +#include "support/allocators/secure.h" +#include "uint256.h" +#include "zcash/Address.hpp" + +#include + +const uint32_t ZIP32_HARDENED_KEY_LIMIT = 0x80000000; +const size_t ZIP32_XFVK_SIZE = 169; +const size_t ZIP32_XSK_SIZE = 169; + +typedef std::vector> RawHDSeed; + +class HDSeed { +private: + RawHDSeed seed; + +public: + HDSeed() {} + HDSeed(RawHDSeed& seedIn) : seed(seedIn) {} + + static HDSeed Random(size_t len = 32); + bool IsNull() const { return seed.empty(); }; + uint256 Fingerprint() const; + RawHDSeed RawSeed() const { return seed; } + + friend bool operator==(const HDSeed& a, const HDSeed& b) + { + return a.seed == b.seed; + } + + friend bool operator!=(const HDSeed& a, const HDSeed& b) + { + return !(a == b); + } +}; + +namespace libzcash { + +typedef blob88 diversifier_index_t; + +struct SaplingExtendedFullViewingKey { + uint8_t depth; + uint32_t parentFVKTag; + uint32_t childIndex; + uint256 chaincode; + libzcash::SaplingFullViewingKey fvk; + uint256 dk; + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(depth); + READWRITE(parentFVKTag); + READWRITE(childIndex); + READWRITE(chaincode); + READWRITE(fvk); + READWRITE(dk); + } + + boost::optional Derive(uint32_t i) const; + + // Returns the first index starting from j that generates a valid + // payment address, along with the corresponding address. Returns + // an error if the diversifier space is exhausted. + boost::optional> + Address(diversifier_index_t j) const; + + libzcash::SaplingPaymentAddress DefaultAddress() const; +}; + +struct SaplingExtendedSpendingKey { + uint8_t depth; + uint32_t parentFVKTag; + uint32_t childIndex; + uint256 chaincode; + libzcash::SaplingExpandedSpendingKey expsk; + uint256 dk; + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(depth); + READWRITE(parentFVKTag); + READWRITE(childIndex); + READWRITE(chaincode); + READWRITE(expsk); + READWRITE(dk); + } + + static SaplingExtendedSpendingKey Master(const HDSeed& seed); + + SaplingExtendedSpendingKey Derive(uint32_t i) const; + + SaplingExtendedFullViewingKey ToXFVK() const; + + libzcash::SaplingPaymentAddress DefaultAddress() const; + + friend bool operator==(const SaplingExtendedSpendingKey& a, const SaplingExtendedSpendingKey& b) + { + return a.depth == b.depth && + a.parentFVKTag == b.parentFVKTag && + a.childIndex == b.childIndex && + a.chaincode == b.chaincode && + a.expsk == b.expsk && + a.dk == b.dk; + } +}; + +} + +#endif // ZCASH_ZIP32_H From 70c8c25e4bafce18cd7114080f3a54bae115f904 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 3 Aug 2018 16:51:01 +0100 Subject: [PATCH 03/12] Store HDSeed in CBasicKeyStore --- src/gtest/test_keystore.cpp | 28 ++++++++++++++++++++++++++++ src/keystore.cpp | 24 ++++++++++++++++++++++++ src/keystore.h | 12 ++++++++++++ 3 files changed, 64 insertions(+) diff --git a/src/gtest/test_keystore.cpp b/src/gtest/test_keystore.cpp index 4b4baa1a4..4f2d0ea44 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 be able to set and retrieve a different seed + ASSERT_TRUE(keyStore.SetHDSeed(seed2)); + ASSERT_TRUE(keyStore.GetHDSeed(seedOut)); + EXPECT_EQ(seed2, 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)); diff --git a/src/keystore.cpp b/src/keystore.cpp index 8e4310b38..efd084259 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -23,6 +23,30 @@ bool CKeyStore::AddKey(const CKey &key) { return AddKeyPubKey(key, key.GetPubKey()); } +bool CBasicKeyStore::SetHDSeed(const HDSeed& seed) +{ + LOCK(cs_SpendingKeyStore); + 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); diff --git a/src/keystore.h b/src/keystore.h index 234f588b8..33ad8807e 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -13,6 +13,7 @@ #include "sync.h" #include "zcash/Address.hpp" #include "zcash/NoteEncryption.hpp" +#include "zcash/zip32.h" #include #include @@ -27,6 +28,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); @@ -109,6 +116,7 @@ typedef std::map Date: Sat, 4 Aug 2018 00:49:11 +0100 Subject: [PATCH 04/12] Store HDSeed in CCryptoKeyStore --- src/gtest/test_keystore.cpp | 60 +++++++++++++++++++++ src/wallet/crypter.cpp | 101 ++++++++++++++++++++++++++++++++++++ src/wallet/crypter.h | 7 +++ 3 files changed, 168 insertions(+) diff --git a/src/gtest/test_keystore.cpp b/src/gtest/test_keystore.cpp index 4f2d0ea44..33d5f0411 100644 --- a/src/gtest/test_keystore.cpp +++ b/src/gtest/test_keystore.cpp @@ -240,6 +240,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 + auto seed2 = HDSeed::Random(); + ASSERT_TRUE(keyStore.SetHDSeed(seed2)); + EXPECT_TRUE(keyStore.HaveHDSeed()); + ASSERT_TRUE(keyStore.GetHDSeed(seedOut)); + EXPECT_EQ(seed2, 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/wallet/crypter.cpp b/src/wallet/crypter.cpp index da8529ba6..798fc14db 100644 --- a/src/wallet/crypter.cpp +++ b/src/wallet/crypter.cpp @@ -121,6 +121,23 @@ static bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector& vchCryptedSecret, + const uint256& seedFp, + HDSeed& seed) +{ + CKeyingMaterial vchSecret; + + // Use seed's fingerprint as IV + // TODO: Handle IV properly when we make encryption a supported feature + if(!DecryptSecret(vMasterKey, vchCryptedSecret, seedFp, vchSecret)) + return false; + + seed = HDSeed(vchSecret); + return seed.Fingerprint() == seedFp; +} + static bool DecryptKey(const CKeyingMaterial& vMasterKey, const std::vector& vchCryptedSecret, const CPubKey& vchPubKey, CKey& key) { CKeyingMaterial vchSecret; @@ -202,6 +219,15 @@ bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn) bool keyPass = false; bool keyFail = false; + { + HDSeed seed; + if (!DecryptHDSeed(vMasterKeyIn, cryptedHDSeed.second, cryptedHDSeed.first, seed)) + { + keyFail = true; + } else { + keyPass = true; + } + } CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin(); for (; mi != mapCryptedKeys.end(); ++mi) { @@ -261,6 +287,67 @@ bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn) return true; } +bool CCryptoKeyStore::SetHDSeed(const HDSeed& seed) +{ + { + LOCK(cs_SpendingKeyStore); + if (!IsCrypted()) { + return CBasicKeyStore::SetHDSeed(seed); + } + + if (IsLocked()) + return false; + + std::vector vchCryptedSecret; + // Use seed's fingerprint as IV + // TODO: Handle this properly when we make encryption a supported feature + auto seedFp = seed.Fingerprint(); + if (!EncryptSecret(vMasterKey, seed.RawSeed(), seedFp, vchCryptedSecret)) + return false; + + // This will call into CWallet to store the crypted seed to disk + if (!SetCryptedHDSeed(seedFp, vchCryptedSecret)) + return false; + } + return true; +} + +bool CCryptoKeyStore::SetCryptedHDSeed( + const uint256& seedFp, + const std::vector& vchCryptedSecret) +{ + { + LOCK(cs_SpendingKeyStore); + if (!IsCrypted()) { + return false; + } + + cryptedHDSeed = std::make_pair(seedFp, vchCryptedSecret); + } + return true; +} + +bool CCryptoKeyStore::HaveHDSeed() const +{ + LOCK(cs_SpendingKeyStore); + if (!IsCrypted()) + return CBasicKeyStore::HaveHDSeed(); + + return !cryptedHDSeed.second.empty(); +} + +bool CCryptoKeyStore::GetHDSeed(HDSeed& seedOut) const +{ + LOCK(cs_SpendingKeyStore); + if (!IsCrypted()) + return CBasicKeyStore::GetHDSeed(seedOut); + + if (cryptedHDSeed.second.empty()) + return false; + + return DecryptHDSeed(vMasterKey, cryptedHDSeed.second, cryptedHDSeed.first, seedOut); +} + bool CCryptoKeyStore::AddKeyPubKey(const CKey& key, const CPubKey &pubkey) { { @@ -463,6 +550,20 @@ bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial& vMasterKeyIn) return false; fUseCrypto = true; + { + std::vector vchCryptedSecret; + // Use seed's fingerprint as IV + // TODO: Handle this properly when we make encryption a supported feature + auto seedFp = hdSeed.Fingerprint(); + if (!EncryptSecret(vMasterKeyIn, hdSeed.RawSeed(), seedFp, vchCryptedSecret)) { + return false; + } + // This will call into CWallet to store the crypted seed to disk + if (!SetCryptedHDSeed(seedFp, vchCryptedSecret)) { + return false; + } + } + hdSeed = HDSeed(); BOOST_FOREACH(KeyMap::value_type& mKey, mapKeys) { const CKey &key = mKey.second; diff --git a/src/wallet/crypter.h b/src/wallet/crypter.h index 169237cc9..11231ead2 100644 --- a/src/wallet/crypter.h +++ b/src/wallet/crypter.h @@ -10,6 +10,7 @@ #include "streams.h" #include "support/allocators/secure.h" #include "zcash/Address.hpp" +#include "zcash/zip32.h" class uint256; @@ -127,6 +128,7 @@ public: class CCryptoKeyStore : public CBasicKeyStore { private: + std::pair> cryptedHDSeed; CryptedKeyMap mapCryptedKeys; CryptedSproutSpendingKeyMap mapCryptedSproutSpendingKeys; CryptedSaplingSpendingKeyMap mapCryptedSaplingSpendingKeys; @@ -172,6 +174,11 @@ public: bool Lock(); + virtual bool SetCryptedHDSeed(const uint256& seedFp, const std::vector &vchCryptedSecret); + bool SetHDSeed(const HDSeed& seed); + bool HaveHDSeed() const; + bool GetHDSeed(HDSeed& seedOut) const; + virtual bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector &vchCryptedSecret); bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey); bool HaveKey(const CKeyID &address) const From e2416930eab5c1bc9f6c9f0b504418c81fa310d3 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 4 Aug 2018 00:54:33 +0100 Subject: [PATCH 05/12] wallet: Store HDSeed and chain data --- src/wallet/wallet.cpp | 86 +++++++++++++++++++++++++++++++++++++++++ src/wallet/wallet.h | 28 ++++++++++++++ src/wallet/walletdb.cpp | 59 ++++++++++++++++++++++++++++ src/wallet/walletdb.h | 39 +++++++++++++++++++ 4 files changed, 212 insertions(+) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 62b344522..a093172b2 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1865,6 +1865,92 @@ CAmount CWallet::GetChange(const CTransaction& tx) const return nChange; } +bool CWallet::IsHDFullyEnabled() const +{ + // Only Sapling addresses are HD for now + return false; +} + +void CWallet::GenerateNewSeed() +{ + LOCK(cs_wallet); + + auto seed = HDSeed::Random(HD_WALLET_SEED_LENGTH); + + int64_t nCreationTime = GetTime(); + + // If the wallet is encrypted and locked, this will fail. + if (!SetHDSeed(seed)) + throw std::runtime_error(std::string(__func__) + ": SetHDSeed failed"); + + // store the key creation time together with + // the child index counter in the database + // as a hdchain object + CHDChain newHdChain; + newHdChain.nVersion = CHDChain::VERSION_HD_BASE; + newHdChain.seedFp = seed.Fingerprint(); + newHdChain.nCreateTime = nCreationTime; + SetHDChain(newHdChain, false); +} + +bool CWallet::SetHDSeed(const HDSeed& seed) +{ + if (!CCryptoKeyStore::SetHDSeed(seed)) { + return false; + } + + if (!fFileBacked) { + return true; + } + + { + LOCK(cs_wallet); + if (!IsCrypted()) { + return CWalletDB(strWalletFile).WriteHDSeed(seed); + } + } + return true; +} + +bool CWallet::SetCryptedHDSeed(const uint256& seedFp, const std::vector &vchCryptedSecret) +{ + if (!CCryptoKeyStore::SetCryptedHDSeed(seedFp, vchCryptedSecret)) { + return false; + } + + if (!fFileBacked) { + return true; + } + + { + LOCK(cs_wallet); + if (pwalletdbEncryption) + return pwalletdbEncryption->WriteCryptedHDSeed(seedFp, vchCryptedSecret); + else + return CWalletDB(strWalletFile).WriteCryptedHDSeed(seedFp, vchCryptedSecret); + } + return false; +} + +void CWallet::SetHDChain(const CHDChain& chain, bool memonly) +{ + LOCK(cs_wallet); + if (!memonly && fFileBacked && !CWalletDB(strWalletFile).WriteHDChain(chain)) + throw std::runtime_error(std::string(__func__) + ": writing chain failed"); + + hdChain = chain; +} + +bool CWallet::LoadHDSeed(const HDSeed& seed) +{ + return CBasicKeyStore::SetHDSeed(seed); +} + +bool CWallet::LoadCryptedHDSeed(const uint256& seedFp, const std::vector& seed) +{ + return CCryptoKeyStore::SetCryptedHDSeed(seedFp, seed); +} + void CWalletTx::SetSproutNoteData(mapSproutNoteData_t ¬eData) { mapSproutNoteData.clear(); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 6c57d6112..00491b5ea 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -23,6 +23,7 @@ #include "wallet/walletdb.h" #include "wallet/rpcwallet.h" #include "zcash/Address.hpp" +#include "zcash/zip32.h" #include "base58.h" #include @@ -61,6 +62,9 @@ static const unsigned int MAX_FREE_TRANSACTION_CREATE_SIZE = 1000; // unless there is some exceptional network disruption. static const unsigned int WITNESS_CACHE_SIZE = MAX_REORG_LENGTH + 1; +//! Size of HD seed in bytes +static const size_t HD_WALLET_SEED_LENGTH = 32; + class CBlockIndex; class CCoinControl; class COutput; @@ -823,6 +827,9 @@ protected: bool UpdatedNoteData(const CWalletTx& wtxIn, CWalletTx& wtx); void MarkAffectedTransactionsDirty(const CTransaction& tx); + /* the hd chain data model (chain counters) */ + CHDChain hdChain; + public: /* * Main wallet lock. @@ -1222,6 +1229,27 @@ public: bool GetBroadcastTransactions() const { return fBroadcastTransactions; } /** Set whether this wallet broadcasts transactions. */ void SetBroadcastTransactions(bool broadcast) { fBroadcastTransactions = broadcast; } + + /* Returns true if HD is enabled for all address types, false if only for Sapling */ + bool IsHDFullyEnabled() const; + + /* Generates a new HD seed (will reset the chain child index counters) + Sets the seed's version based on the current wallet version (so the + caller must ensure the current wallet version is correct before calling + this function). */ + void GenerateNewSeed(); + + bool SetHDSeed(const HDSeed& seed); + bool SetCryptedHDSeed(const uint256& seedFp, const std::vector &vchCryptedSecret); + + /* Set the HD chain model (chain child index counters) */ + void SetHDChain(const CHDChain& chain, bool memonly); + const CHDChain& GetHDChain() const { return hdChain; } + + /* Set the current HD seed, without saving it to disk (used by LoadWallet) */ + bool LoadHDSeed(const HDSeed& key); + /* Set the current encrypted HD seed, without saving it to disk (used by LoadWallet) */ + bool LoadCryptedHDSeed(const uint256& seedFp, const std::vector& seed); /* Find notes filtered by payment address, min depth, ability to spend */ void GetFilteredNotes(std::vector& sproutEntries, diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 0281c5e3f..699d71946 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -708,6 +708,45 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, { ssValue >> pwallet->nWitnessCacheSize; } + else if (strType == "hdseed") + { + uint256 seedFp; + RawHDSeed rawSeed; + ssKey >> seedFp; + ssValue >> rawSeed; + HDSeed seed(rawSeed); + + if (seed.Fingerprint() != seedFp) + { + strErr = "Error reading wallet database: HDSeed corrupt"; + return false; + } + + if (!pwallet->LoadHDSeed(seed)) + { + strErr = "Error reading wallet database: LoadHDSeed failed"; + return false; + } + } + else if (strType == "chdseed") + { + uint256 seedFp; + vector vchCryptedSecret; + ssKey >> seedFp; + ssValue >> vchCryptedSecret; + if (!pwallet->LoadCryptedHDSeed(seedFp, vchCryptedSecret)) + { + strErr = "Error reading wallet database: LoadCryptedSeed failed"; + return false; + } + wss.fIsEncrypted = true; + } + else if (strType == "hdchain") + { + CHDChain chain; + ssValue >> chain; + pwallet->SetHDChain(chain, true); + } } catch (...) { return false; @@ -718,6 +757,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, static bool IsKeyType(string strType) { return (strType== "key" || strType == "wkey" || + strType == "hdseed" || strType == "chdseed" || strType == "zkey" || strType == "czkey" || strType == "vkey" || strType == "mkey" || strType == "ckey"); @@ -1103,3 +1143,22 @@ bool CWalletDB::EraseDestData(const std::string &address, const std::string &key nWalletDBUpdated++; return Erase(std::make_pair(std::string("destdata"), std::make_pair(address, key))); } + + +bool CWalletDB::WriteHDSeed(const HDSeed& seed) +{ + nWalletDBUpdated++; + return Write(std::make_pair(std::string("hdseed"), seed.Fingerprint()), seed.RawSeed()); +} + +bool CWalletDB::WriteCryptedHDSeed(const uint256& seedFp, const std::vector& vchCryptedSecret) +{ + nWalletDBUpdated++; + return Write(std::make_pair(std::string("chdseed"), seedFp), vchCryptedSecret); +} + +bool CWalletDB::WriteHDChain(const CHDChain& chain) +{ + nWalletDBUpdated++; + return Write(std::string("hdchain"), chain); +} diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 2dd301ed9..7cd446e9b 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -11,6 +11,7 @@ #include "key.h" #include "keystore.h" #include "zcash/Address.hpp" +#include "zcash/zip32.h" #include #include @@ -40,6 +41,39 @@ enum DBErrors DB_NEED_REWRITE }; +/* simple hd chain data model */ +class CHDChain +{ +public: + static const int VERSION_HD_BASE = 1; + static const int CURRENT_VERSION = VERSION_HD_BASE; + int nVersion; + uint256 seedFp; + int64_t nCreateTime; // 0 means unknown + uint32_t saplingAccountCounter; + + CHDChain() { SetNull(); } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(nVersion); + READWRITE(seedFp); + READWRITE(nCreateTime); + READWRITE(saplingAccountCounter); + } + + void SetNull() + { + nVersion = CHDChain::CURRENT_VERSION; + seedFp.SetNull(); + nCreateTime = 0; + saplingAccountCounter = 0; + } +}; + class CKeyMetadata { public: @@ -132,6 +166,11 @@ public: static bool Recover(CDBEnv& dbenv, const std::string& filename, bool fOnlyKeys); static bool Recover(CDBEnv& dbenv, const std::string& filename); + bool WriteHDSeed(const HDSeed& seed); + bool WriteCryptedHDSeed(const uint256& seedFp, const std::vector& vchCryptedSecret); + //! write the hdchain model (external chain child index counter) + bool WriteHDChain(const CHDChain& chain); + /// Write spending key to wallet database, where key is payment address and value is spending key. bool WriteZKey(const libzcash::SproutPaymentAddress& addr, const libzcash::SproutSpendingKey& key, const CKeyMetadata &keyMeta); bool WriteCryptedZKey(const libzcash::SproutPaymentAddress & addr, From a4ac4fc3f413b65fbf9c3a12081ce6d287819e9b Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Mon, 27 Aug 2018 16:47:17 +0100 Subject: [PATCH 06/12] wallet: Store Sapling key metadata indexed by ivk Closes #3426. --- src/wallet/rpcdump.cpp | 19 +++++++++++++------ src/wallet/wallet.cpp | 5 +++-- src/wallet/wallet.h | 2 +- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index e4a6e179d..1ed79d0f3 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -554,8 +554,10 @@ class AddSpendingKeyToWallet : public boost::static_visitor { private: CWallet *m_wallet; + const Consensus::Params ¶ms; public: - AddSpendingKeyToWallet(CWallet *wallet) : m_wallet(wallet) {} + AddSpendingKeyToWallet(CWallet *wallet, const Consensus::Params ¶ms) : + m_wallet(wallet), params(params) {} bool operator()(const libzcash::SproutSpendingKey &sk) const { auto addr = sk.address(); @@ -577,6 +579,7 @@ public: bool operator()(const libzcash::SaplingSpendingKey &sk) const { auto fvk = sk.full_viewing_key(); + auto ivk = fvk.in_viewing_key(); auto addr = sk.default_address(); { // Don't throw error in case a key is already there @@ -588,9 +591,12 @@ public: if (!m_wallet-> AddSaplingZKey(sk, addr)) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding spending key to wallet"); } - - m_wallet->mapSaplingZKeyMetadata[addr].nCreateTime = 1; - + + // Sapling addresses can't have been used in transactions prior to activation. + m_wallet->mapSaplingZKeyMetadata[ivk].nCreateTime = std::max( + 1, // In case a code fork sets Sapling to always be active + params.vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight); + return false; } } @@ -672,9 +678,10 @@ UniValue z_importkey(const UniValue& params, bool fHelp) if (!IsValidSpendingKey(spendingkey)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid spending key"); } - + // Sapling support - auto keyAlreadyExists = boost::apply_visitor(AddSpendingKeyToWallet(pwalletMain), spendingkey); + auto keyAlreadyExists = boost::apply_visitor( + AddSpendingKeyToWallet(pwalletMain, Params().GetConsensus()), spendingkey); if (keyAlreadyExists && fIgnoreExistingKey) { return NullUniValue; } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index a093172b2..ed7830ec4 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -107,6 +107,7 @@ SaplingPaymentAddress CWallet::GenerateNewSaplingZKey() auto sk = SaplingSpendingKey::random(); auto fvk = sk.full_viewing_key(); + auto ivk = fvk.in_viewing_key(); auto addr = sk.default_address(); // Check for collision, even though it is unlikely to ever occur @@ -116,8 +117,8 @@ SaplingPaymentAddress CWallet::GenerateNewSaplingZKey() // Create new metadata int64_t nCreationTime = GetTime(); - mapSaplingZKeyMetadata[addr] = CKeyMetadata(nCreationTime); - + mapSaplingZKeyMetadata[ivk] = CKeyMetadata(nCreationTime); + if (!AddSaplingZKey(sk, addr)) { throw std::runtime_error("CWallet::GenerateNewSaplingZKey(): AddSaplingZKey failed"); } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 00491b5ea..f9026937b 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -846,7 +846,7 @@ public: std::set setKeyPool; std::map mapKeyMetadata; std::map mapZKeyMetadata; - std::map mapSaplingZKeyMetadata; + std::map mapSaplingZKeyMetadata; typedef std::map MasterKeyMap; MasterKeyMap mapMasterKeys; From 70b4ad2dcd07effd65c034d8a3351849b2d919a5 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 29 Aug 2018 00:07:07 +0100 Subject: [PATCH 07/12] wallet: Switch from SaplingSpendingKey to SaplingExtendedSpendingKey The wallet now only stores Sapling extended spending keys, and thus can only be used with keys generated from an HDSeed via ZIP 32. Note that not all Sapling keys in the wallet will correspond to the wallet's HDSeed, as a standalone Sapling xsk can be imported via z_importkey. However, it must have been generated from a seed itself, and thus is more likely to be backed up elsewhere. --- src/chainparams.cpp | 6 +- src/chainparams.h | 2 +- src/gtest/test_keys.cpp | 17 +++-- src/gtest/test_keystore.cpp | 10 ++- src/key_io.cpp | 12 +-- src/key_io.h | 1 + src/keystore.cpp | 4 +- src/keystore.h | 11 ++- src/test/key_tests.cpp | 16 ++-- src/test/rpc_wallet_tests.cpp | 10 ++- src/wallet/asyncrpcoperation_sendmany.cpp | 4 +- src/wallet/crypter.cpp | 18 ++--- src/wallet/crypter.h | 4 +- src/wallet/gtest/test_wallet.cpp | 89 ++++++++++++++--------- src/wallet/gtest/test_wallet_zkeys.cpp | 23 ++++-- src/wallet/rpcdump.cpp | 10 +-- src/wallet/rpcwallet.cpp | 1 + src/wallet/wallet.cpp | 55 +++++++++----- src/wallet/wallet.h | 2 +- src/zcash/Address.cpp | 4 - src/zcash/Address.hpp | 4 - src/zcash/zip32.cpp | 4 + src/zcash/zip32.h | 5 ++ 23 files changed, 190 insertions(+), 122 deletions(-) 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 33d5f0411..7059f2fc8 100644 --- a/src/gtest/test_keystore.cpp +++ b/src/gtest/test_keystore.cpp @@ -194,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)); 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