From d65200c8601c16c35f6207d8648f6771867182e9 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Fri, 19 Nov 2021 14:12:56 -0700 Subject: [PATCH] Refine structure of Zcash address generation. This commit does not include functional changes; it merely breaks up content previously included in `zcash/address/zip32.h` into smaller and more focused components. --- src/Makefile.am | 10 +- src/keystore.h | 1 + src/wallet/gtest/test_orchard_zkeys.cpp | 1 + src/wallet/wallet.h | 2 + src/zcash/Address.hpp | 1 + src/zcash/address/bip44.cpp | 70 ++++++++ src/zcash/address/bip44.h | 36 ++++ src/zcash/address/mnemonic.cpp | 37 ++++ src/zcash/address/mnemonic.h | 107 ++++++++++++ src/zcash/address/unified.cpp | 73 ++++++++ src/zcash/address/unified.h | 92 ++++++++++ src/zcash/address/zip32.cpp | 162 +----------------- src/zcash/address/zip32.h | 214 ++---------------------- 13 files changed, 442 insertions(+), 364 deletions(-) create mode 100644 src/zcash/address/bip44.cpp create mode 100644 src/zcash/address/bip44.h create mode 100644 src/zcash/address/mnemonic.cpp create mode 100644 src/zcash/address/mnemonic.h create mode 100644 src/zcash/address/unified.cpp create mode 100644 src/zcash/address/unified.h diff --git a/src/Makefile.am b/src/Makefile.am index aa3213ff6..bca93e6ce 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -107,9 +107,12 @@ LIBZCASH_H = \ zcash/IncrementalMerkleTree.hpp \ zcash/NoteEncryption.hpp \ zcash/Address.hpp \ + zcash/address/bip44.h \ + zcash/address/mnemonic.h \ + zcash/address/orchard.h \ zcash/address/sapling.hpp \ zcash/address/sprout.hpp \ - zcash/address/orchard.h \ + zcash/address/unified.h \ zcash/address/zip32.h \ zcash/History.hpp \ zcash/JoinSplit.hpp \ @@ -536,9 +539,12 @@ libzcash_a_SOURCES = \ zcash/IncrementalMerkleTree.cpp \ zcash/NoteEncryption.cpp \ zcash/Address.cpp \ + zcash/address/bip44.cpp \ + zcash/address/mnemonic.cpp \ + zcash/address/orchard.cpp \ zcash/address/sapling.cpp \ zcash/address/sprout.cpp \ - zcash/address/orchard.cpp \ + zcash/address/unified.cpp \ zcash/address/zip32.cpp \ zcash/History.cpp \ zcash/JoinSplit.cpp \ diff --git a/src/keystore.h b/src/keystore.h index 78bf5c400..c2e40b399 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -11,6 +11,7 @@ #include "script/script.h" #include "script/standard.h" #include "sync.h" +#include "zcash/address/mnemonic.h" #include "zcash/Address.hpp" #include "zcash/NoteEncryption.hpp" diff --git a/src/wallet/gtest/test_orchard_zkeys.cpp b/src/wallet/gtest/test_orchard_zkeys.cpp index bdf84f66c..3afd22f0a 100644 --- a/src/wallet/gtest/test_orchard_zkeys.cpp +++ b/src/wallet/gtest/test_orchard_zkeys.cpp @@ -4,6 +4,7 @@ #include +#include "zcash/address/mnemonic.h" #include "zcash/address/orchard.hpp" TEST(OrchardZkeysTest, IVKSerializationRoundtrip) { diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 3bb5c55d1..6bec30f9e 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -23,6 +23,8 @@ #include "wallet/crypter.h" #include "wallet/walletdb.h" #include "wallet/rpcwallet.h" +#include "zcash/address/bip44.h" +#include "zcash/address/mnemonic.h" #include "zcash/Address.hpp" #include "zcash/Note.hpp" #include "base58.h" diff --git a/src/zcash/Address.hpp b/src/zcash/Address.hpp index e41272a90..3200e9833 100644 --- a/src/zcash/Address.hpp +++ b/src/zcash/Address.hpp @@ -5,6 +5,7 @@ #include "zcash/address/orchard.hpp" #include "zcash/address/sapling.hpp" #include "zcash/address/sprout.hpp" +#include "zcash/address/unified.h" #include "zcash/address/zip32.h" #include diff --git a/src/zcash/address/bip44.cpp b/src/zcash/address/bip44.cpp new file mode 100644 index 000000000..f3e8b69e8 --- /dev/null +++ b/src/zcash/address/bip44.cpp @@ -0,0 +1,70 @@ +// Copyright (c) 2021 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php . + +#include "bip44.h" + +std::optional> libzcash::DeriveBip44TransparentAccountKey(const HDSeed& seed, uint32_t bip44CoinType, libzcash::AccountId accountId) { + auto rawSeed = seed.RawSeed(); + auto m = CExtKey::Master(rawSeed.data(), rawSeed.size()); + + // We use a fixed keypath scheme of m/44'/coin_type'/account' + // Derive m/44' + auto m_44h = m.Derive(44 | HARDENED_KEY_LIMIT); + if (!m_44h.has_value()) return std::nullopt; + + // Derive m/44'/coin_type' + auto m_44h_cth = m_44h.value().Derive(bip44CoinType | HARDENED_KEY_LIMIT); + if (!m_44h_cth.has_value()) return std::nullopt; + + // Derive m/44'/coin_type'/account_id' + auto result = m_44h_cth.value().Derive(accountId | HARDENED_KEY_LIMIT); + if (!result.has_value()) return std::nullopt; + + auto hdKeypath = "m/44'/" + std::to_string(bip44CoinType) + "'/" + std::to_string(accountId) + "'"; + + return std::make_pair(result.value(), hdKeypath); +} + +std::optional libzcash::Bip44AccountChains::ForAccount( + const HDSeed& seed, + uint32_t bip44CoinType, + libzcash::AccountId accountId) { + auto accountKeyOpt = DeriveBip44TransparentAccountKey(seed, bip44CoinType, accountId); + if (!accountKeyOpt.has_value()) return std::nullopt; + + auto accountKey = accountKeyOpt.value(); + auto external = accountKey.first.Derive(0); + auto internal = accountKey.first.Derive(1); + + if (!(external.has_value() && internal.has_value())) return std::nullopt; + + return Bip44AccountChains(seed.Fingerprint(), bip44CoinType, accountId, external.value(), internal.value()); +} + +std::optional> libzcash::Bip44AccountChains::DeriveExternal(uint32_t addrIndex) { + auto childKey = external.Derive(addrIndex); + if (!childKey.has_value()) return std::nullopt; + + auto hdKeypath = "m/44'/" + + std::to_string(bip44CoinType) + "'/" + + std::to_string(accountId) + "'/" + + "0/" + + std::to_string(addrIndex); + + return std::make_pair(childKey.value(), hdKeypath); +} + +std::optional> libzcash::Bip44AccountChains::DeriveInternal(uint32_t addrIndex) { + auto childKey = internal.Derive(addrIndex); + if (!childKey.has_value()) return std::nullopt; + + auto hdKeypath = "m/44'/" + + std::to_string(bip44CoinType) + "'/" + + std::to_string(accountId) + "'/" + + "1/" + + std::to_string(addrIndex); + + return std::make_pair(childKey.value(), hdKeypath); +} + diff --git a/src/zcash/address/bip44.h b/src/zcash/address/bip44.h new file mode 100644 index 000000000..4a6bc15c9 --- /dev/null +++ b/src/zcash/address/bip44.h @@ -0,0 +1,36 @@ +// Copyright (c) 2021 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php . + +#ifndef ZCASH_ZCASH_ADDRESS_BIP44_H +#define ZCASH_ZCASH_ADDRESS_BIP44_H + +#include "zip32.h" + +namespace libzcash { + +std::optional> DeriveBip44TransparentAccountKey(const HDSeed& seed, uint32_t bip44CoinType, libzcash::AccountId accountId); + +class Bip44AccountChains { +private: + uint256 seedFp; + libzcash::AccountId accountId; + uint32_t bip44CoinType; + CExtKey external; + CExtKey internal; + + Bip44AccountChains(uint256 seedFpIn, uint32_t bip44CoinTypeIn, libzcash::AccountId accountIdIn, CExtKey externalIn, CExtKey internalIn): + seedFp(seedFpIn), accountId(accountIdIn), bip44CoinType(bip44CoinTypeIn), external(externalIn), internal(internalIn) {} +public: + static std::optional ForAccount( + const HDSeed& mnemonic, + uint32_t bip44CoinType, + libzcash::AccountId accountId); + + std::optional> DeriveExternal(uint32_t addrIndex); + std::optional> DeriveInternal(uint32_t addrIndex); +}; + +} //namespace libzcash + +#endif // ZCASH_ZCASH_ADDRESS_BIP44_H diff --git a/src/zcash/address/mnemonic.cpp b/src/zcash/address/mnemonic.cpp new file mode 100644 index 000000000..97ac24389 --- /dev/null +++ b/src/zcash/address/mnemonic.cpp @@ -0,0 +1,37 @@ +// Copyright (c) 2018-2021 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php . + +#include "random.h" + +#include "mnemonic.h" + +#include "bip44.h" +#include "unified.h" + +using namespace libzcash; + +MnemonicSeed MnemonicSeed::Random(uint32_t bip44CoinType, Language language, size_t entropyLen) +{ + assert(entropyLen >= 32); + while (true) { // loop until we find usable entropy + RawHDSeed entropy(entropyLen, 0); + GetRandBytes(entropy.data(), entropyLen); + const char* phrase = zip339_entropy_to_phrase(language, entropy.data(), entropyLen); + SecureString mnemonic(phrase); + zip339_free_phrase(phrase); + MnemonicSeed seed(language, mnemonic); + + // Verify that the seed data is valid entropy for unified spending keys at + // account 0 and at both the public & private chain levels for account 0x7FFFFFFF. + // It is not necessary to check for a valid diversified Sapling address at + // account 0x7FFFFFFF because derivation via the legacy path can simply search + // for a valid diversifier; unlike in the unified spending key case, diversifier + // indices don't need to line up with anything. + if (ZcashdUnifiedSpendingKey::ForAccount(seed, bip44CoinType, 0).has_value() && + Bip44AccountChains::ForAccount(seed, bip44CoinType, ZCASH_LEGACY_ACCOUNT).has_value()) { + return seed; + } + } +} + diff --git a/src/zcash/address/mnemonic.h b/src/zcash/address/mnemonic.h new file mode 100644 index 000000000..db9f6a517 --- /dev/null +++ b/src/zcash/address/mnemonic.h @@ -0,0 +1,107 @@ +// Copyright (c) 2021 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php . + +#ifndef ZCASH_ZCASH_ADDRESS_MNEMONIC_H +#define ZCASH_ZCASH_ADDRESS_MNEMONIC_H + +#include "zip32.h" + +class MnemonicSeed: public HDSeed { +private: + Language language; + SecureString mnemonic; + + MnemonicSeed() {} + + void SetSeedFromMnemonic() { + seed.resize(64); + zip339_phrase_to_seed(language, mnemonic.c_str(), (uint8_t (*)[64])seed.data()); + } + +public: + MnemonicSeed(Language languageIn, SecureString mnemonicIn): language(languageIn), mnemonic(mnemonicIn) { + SetSeedFromMnemonic(); + } + + /** + * Randomly generate a new mnemonic seed. A SLIP-44 coin type is required to make it possible + * to check that the generated seed can produce valid transparent and unified addresses at account + * numbers 0x7FFFFFFF and 0x00 respectively. + */ + static MnemonicSeed Random(uint32_t bip44CoinType, Language language = English, size_t entropyLen = 32); + + static std::string LanguageName(Language language) { + switch (language) { + case English: + return "English"; + case SimplifiedChinese: + return "Simplified Chinese"; + case TraditionalChinese: + return "Traditional Chinese"; + case Czech: + return "Czech"; + case French: + return "French"; + case Italian: + return "Italian"; + case Japanese: + return "Japanese"; + case Korean: + return "Korean"; + case Portuguese: + return "Portuguese"; + case Spanish: + return "Spanish"; + default: + return "INVALID"; + } + } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + if (ser_action.ForRead()) { + uint32_t language0; + + READWRITE(language0); + READWRITE(mnemonic); + language = (Language) language0; + SetSeedFromMnemonic(); + } else { + uint32_t language0 = (uint32_t) language; + + READWRITE(language0); + READWRITE(mnemonic); + } + } + + template + static MnemonicSeed Read(Stream& stream) { + MnemonicSeed seed; + stream >> seed; + return seed; + } + + const Language GetLanguage() const { + return language; + } + + const SecureString& GetMnemonic() const { + return mnemonic; + } + + friend bool operator==(const MnemonicSeed& a, const MnemonicSeed& b) + { + return a.seed == b.seed; + } + + friend bool operator!=(const MnemonicSeed& a, const MnemonicSeed& b) + { + return !(a == b); + } +}; + +#endif // ZCASH_ZCASH_ADDRESS_MNEMONIC_H + diff --git a/src/zcash/address/unified.cpp b/src/zcash/address/unified.cpp new file mode 100644 index 000000000..27067e6b1 --- /dev/null +++ b/src/zcash/address/unified.cpp @@ -0,0 +1,73 @@ +// Copyright (c) 2021 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php . + +#include "unified.h" +#include "bip44.h" + +using namespace libzcash; + +// +// Unified Keys +// + +std::optional> ZcashdUnifiedSpendingKey::ForAccount(const HDSeed& seed, uint32_t bip44CoinType, AccountId accountId) { + ZcashdUnifiedSpendingKey usk; + usk.accountId = accountId; + + auto transparentKey = DeriveBip44TransparentAccountKey(seed, bip44CoinType, accountId); + if (!transparentKey.has_value()) return std::nullopt; + usk.transparentKey = transparentKey.value().first; + + auto saplingKey = SaplingExtendedSpendingKey::ForAccount(seed, bip44CoinType, accountId); + usk.saplingKey = saplingKey.first; + + return std::make_pair(usk, saplingKey.second); +} + +ZcashdUnifiedFullViewingKey ZcashdUnifiedSpendingKey::ToFullViewingKey() const { + ZcashdUnifiedFullViewingKey ufvk; + + if (transparentKey.has_value()) { + ufvk.transparentKey = transparentKey.value().Neuter(); + } + + if (saplingKey.has_value()) { + ufvk.saplingKey = saplingKey.value().ToXFVK(); + } + + return ufvk; +} + +std::optional ZcashdUnifiedFullViewingKey::Address(diversifier_index_t j) const { + ZcashdUnifiedAddress ua; + + if (transparentKey.has_value()) { + auto childIndex = j.ToTransparentChildIndex(); + if (!childIndex.has_value()) return std::nullopt; + + CExtPubKey externalKey; + if (!transparentKey.value().Derive(externalKey, 0)) { + return std::nullopt; + } + + CExtPubKey childKey; + if (externalKey.Derive(childKey, childIndex.value())) { + ua.transparentAddress = childKey.pubkey.GetID(); + } else { + return std::nullopt; + } + } + + if (saplingKey.has_value()) { + auto saplingAddress = saplingKey.value().Address(j); + if (saplingAddress.has_value()) { + ua.saplingAddress = saplingAddress.value(); + } else { + return std::nullopt; + } + } + + return ua; +} + diff --git a/src/zcash/address/unified.h b/src/zcash/address/unified.h new file mode 100644 index 000000000..08269b771 --- /dev/null +++ b/src/zcash/address/unified.h @@ -0,0 +1,92 @@ +// Copyright (c) 2021 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php . + +#ifndef ZCASH_ZCASH_ADDRESS_UNIFIED_H +#define ZCASH_ZCASH_ADDRESS_UNIFIED_H + +#include "zip32.h" +#include "bip44.h" + +namespace libzcash { + +class ZcashdUnifiedSpendingKey; +class ZcashdUnifiedFullViewingKey; + +class ZcashdUnifiedAddress { +private: + diversifier_index_t diversifier_index; + std::optional transparentAddress; + std::optional saplingAddress; + + friend class ZcashdUnifiedFullViewingKey; + + ZcashdUnifiedAddress() {} +public: + const std::optional& GetTransparentAddress() const { + return transparentAddress; + } + + const std::optional& GetSaplingPaymentAddress() const { + return saplingAddress; + } +}; + +class ZcashdUnifiedFullViewingKey { +private: + std::optional transparentKey; + std::optional saplingKey; + + ZcashdUnifiedFullViewingKey() {} + + friend class ZcashdUnifiedSpendingKey; +public: + const std::optional& GetTransparentKey() const { + return transparentKey; + } + + const std::optional& GetSaplingExtendedFullViewingKey() const { + return saplingKey; + } + + std::optional Address(diversifier_index_t j) const; + + std::pair FindAddress(diversifier_index_t j) const { + auto addr = Address(j); + while (!addr.has_value()) { + if (!j.increment()) + throw std::runtime_error(std::string(__func__) + ": diversifier index overflow.");; + addr = Address(j); + } + return std::make_pair(addr.value(), j); + } +}; + +class ZcashdUnifiedSpendingKey { +private: + libzcash::AccountId accountId; + std::optional transparentKey; + std::optional saplingKey; + + ZcashdUnifiedSpendingKey() {} +public: + static std::optional> ForAccount( + const HDSeed& seed, + uint32_t bip44CoinType, + libzcash::AccountId accountId); + + const std::optional& GetTransparentKey() const { + return transparentKey; + } + + const std::optional& GetSaplingExtendedSpendingKey() const { + return saplingKey; + } + + ZcashdUnifiedFullViewingKey ToFullViewingKey() const; +}; + +} //namespace libzcash + +#endif // ZCASH_ZCASH_ADDRESS_UNIFIED_H + diff --git a/src/zcash/address/zip32.cpp b/src/zcash/address/zip32.cpp index f88311fde..fc558edef 100644 --- a/src/zcash/address/zip32.cpp +++ b/src/zcash/address/zip32.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2018 The Zcash developers +// Copyright (c) 2018-2021 The Zcash developers // Distributed under the MIT software license, see the accompanying // file COPYING or https://www.opensource.org/licenses/mit-license.php . @@ -21,30 +21,6 @@ const unsigned char ZCASH_TADDR_OVK_PERSONAL[BLAKE2bPersonalBytes] = const libzcash::diversifier_index_t MAX_TRANSPARENT_CHILD_IDX(0x7FFFFFFF); -MnemonicSeed MnemonicSeed::Random(uint32_t bip44CoinType, Language language, size_t entropyLen) -{ - assert(entropyLen >= 32); - while (true) { // loop until we find usable entropy - RawHDSeed entropy(entropyLen, 0); - GetRandBytes(entropy.data(), entropyLen); - const char* phrase = zip339_entropy_to_phrase(language, entropy.data(), entropyLen); - SecureString mnemonic(phrase); - zip339_free_phrase(phrase); - MnemonicSeed seed(language, mnemonic); - - // Verify that the seed data is valid entropy for unified spending keys at - // account 0 and at both the public & private chain levels for account 0x7FFFFFFF. - // It is not necessary to check for a valid diversified Sapling address at - // account 0x7FFFFFFF because derivation via the legacy path can simply search - // for a valid diversifier; unlike in the unified spending key case, diversifier - // indices don't need to line up with anything. - if (libzcash::ZcashdUnifiedSpendingKey::ForAccount(seed, bip44CoinType, 0).has_value() && - libzcash::Bip44AccountChains::ForAccount(seed, bip44CoinType, ZCASH_LEGACY_ACCOUNT).has_value()) { - return seed; - } - } -} - uint256 HDSeed::Fingerprint() const { CBLAKE2bWriter h(SER_GETHASH, 0, ZCASH_HD_SEED_FP_PERSONAL); @@ -81,74 +57,6 @@ std::optional diversifier_index_t::ToTransparentChildIndex() const } } -// -// Transparent -// - -std::optional> DeriveBip44TransparentAccountKey(const MnemonicSeed& seed, uint32_t bip44CoinType, libzcash::AccountId accountId) { - auto rawSeed = seed.RawSeed(); - auto m = CExtKey::Master(rawSeed.data(), rawSeed.size()); - - // We use a fixed keypath scheme of m/44'/coin_type'/account' - // Derive m/44' - auto m_44h = m.Derive(44 | HARDENED_KEY_LIMIT); - if (!m_44h.has_value()) return std::nullopt; - - // Derive m/44'/coin_type' - auto m_44h_cth = m_44h.value().Derive(bip44CoinType | HARDENED_KEY_LIMIT); - if (!m_44h_cth.has_value()) return std::nullopt; - - // Derive m/44'/coin_type'/account_id' - auto result = m_44h_cth.value().Derive(accountId | HARDENED_KEY_LIMIT); - if (!result.has_value()) return std::nullopt; - - auto hdKeypath = "m/44'/" + std::to_string(bip44CoinType) + "'/" + std::to_string(accountId) + "'"; - - return std::make_pair(result.value(), hdKeypath); -} - -std::optional Bip44AccountChains::ForAccount( - const MnemonicSeed& seed, - uint32_t bip44CoinType, - libzcash::AccountId accountId) { - auto accountKeyOpt = DeriveBip44TransparentAccountKey(seed, bip44CoinType, accountId); - if (!accountKeyOpt.has_value()) return std::nullopt; - - auto accountKey = accountKeyOpt.value(); - auto external = accountKey.first.Derive(0); - auto internal = accountKey.first.Derive(1); - - if (!(external.has_value() && internal.has_value())) return std::nullopt; - - return Bip44AccountChains(seed.Fingerprint(), bip44CoinType, accountId, external.value(), internal.value()); -} - -std::optional> Bip44AccountChains::DeriveExternal(uint32_t addrIndex) { - auto childKey = external.Derive(addrIndex); - if (!childKey.has_value()) return std::nullopt; - - auto hdKeypath = "m/44'/" - + std::to_string(bip44CoinType) + "'/" - + std::to_string(accountId) + "'/" - + "0/" - + std::to_string(addrIndex); - - return std::make_pair(childKey.value(), hdKeypath); -} - -std::optional> Bip44AccountChains::DeriveInternal(uint32_t addrIndex) { - auto childKey = internal.Derive(addrIndex); - if (!childKey.has_value()) return std::nullopt; - - auto hdKeypath = "m/44'/" - + std::to_string(bip44CoinType) + "'/" - + std::to_string(accountId) + "'/" - + "1/" - + std::to_string(addrIndex); - - return std::make_pair(childKey.value(), hdKeypath); -} - // // Sapling // @@ -251,7 +159,7 @@ SaplingExtendedSpendingKey SaplingExtendedSpendingKey::Derive(uint32_t i) const return xsk_i; } -std::pair SaplingExtendedSpendingKey::ForAccount(const MnemonicSeed& seed, uint32_t bip44CoinType, libzcash::AccountId accountId) { +std::pair SaplingExtendedSpendingKey::ForAccount(const HDSeed& seed, uint32_t bip44CoinType, libzcash::AccountId accountId) { auto m = Master(seed); // We use a fixed keypath scheme of m/32'/coin_type'/account' @@ -269,7 +177,7 @@ std::pair SaplingExtendedSpendingKey::For return std::make_pair(xsk, hdKeypath); } -std::pair SaplingExtendedSpendingKey::Legacy(const MnemonicSeed& seed, uint32_t bip44CoinType, uint32_t addressIndex) { +std::pair SaplingExtendedSpendingKey::Legacy(const HDSeed& seed, uint32_t bip44CoinType, uint32_t addressIndex) { auto m = Master(seed); // We use a fixed keypath scheme of m/32'/coin_type'/0x7FFFFFFF'/addressIndex' @@ -309,70 +217,6 @@ SaplingExtendedFullViewingKey SaplingExtendedSpendingKey::ToXFVK() const return ret; } -// -// Unified -// - -std::optional> ZcashdUnifiedSpendingKey::ForAccount(const MnemonicSeed& seed, uint32_t bip44CoinType, libzcash::AccountId accountId) { - ZcashdUnifiedSpendingKey usk; - usk.accountId = accountId; - - auto transparentKey = DeriveBip44TransparentAccountKey(seed, bip44CoinType, accountId); - if (!transparentKey.has_value()) return std::nullopt; - usk.transparentKey = transparentKey.value().first; - - auto saplingKey = SaplingExtendedSpendingKey::ForAccount(seed, bip44CoinType, accountId); - usk.saplingKey = saplingKey.first; - - return std::make_pair(usk, saplingKey.second); -} - -ZcashdUnifiedFullViewingKey ZcashdUnifiedSpendingKey::ToFullViewingKey() const { - ZcashdUnifiedFullViewingKey ufvk; - - if (transparentKey.has_value()) { - ufvk.transparentKey = transparentKey.value().Neuter(); - } - - if (saplingKey.has_value()) { - ufvk.saplingKey = saplingKey.value().ToXFVK(); - } - - return ufvk; -} - -std::optional ZcashdUnifiedFullViewingKey::Address(diversifier_index_t j) const { - ZcashdUnifiedAddress ua; - - if (transparentKey.has_value()) { - auto childIndex = j.ToTransparentChildIndex(); - if (!childIndex.has_value()) return std::nullopt; - - CExtPubKey externalKey; - if (!transparentKey.value().Derive(externalKey, 0)) { - return std::nullopt; - } - - CExtPubKey childKey; - if (externalKey.Derive(childKey, childIndex.value())) { - ua.transparentAddress = childKey.pubkey.GetID(); - } else { - return std::nullopt; - } - } - - if (saplingKey.has_value()) { - auto saplingAddress = saplingKey.value().Address(j); - if (saplingAddress.has_value()) { - ua.saplingAddress = saplingAddress.value(); - } else { - return std::nullopt; - } - } - - return ua; -} - std::optional ParseHDKeypathAccount(uint32_t purpose, uint32_t coinType, const std::string& keyPath) { std::regex pattern("m/" + std::to_string(purpose) + "'/" + std::to_string(coinType) + "'/([0-9]+)'.*"); std::smatch matches; diff --git a/src/zcash/address/zip32.h b/src/zcash/address/zip32.h index 9da16b71e..7f9cf4cc9 100644 --- a/src/zcash/address/zip32.h +++ b/src/zcash/address/zip32.h @@ -1,4 +1,4 @@ -// Copyright (c) 2018 The Zcash developers +// Copyright (c) 2018-2021 The Zcash developers // Distributed under the MIT software license, see the accompanying // file COPYING or https://www.opensource.org/licenses/mit-license.php . @@ -22,13 +22,6 @@ const uint32_t HARDENED_KEY_LIMIT = 0x80000000; const size_t ZIP32_XFVK_SIZE = 169; const size_t ZIP32_XSK_SIZE = 169; -/** - * The account identifier used for HD derivation of - * transparent and Sapling addresses via the legacy - * `getnewaddress` and `z_getnewaddress` code paths, - */ -const uint32_t ZCASH_LEGACY_ACCOUNT = HARDENED_KEY_LIMIT - 1; - typedef std::vector> RawHDSeed; typedef std::string HDKeyPath; @@ -64,102 +57,6 @@ public: }; -class MnemonicSeed: public HDSeed { -private: - Language language; - SecureString mnemonic; - - MnemonicSeed() {} - - void SetSeedFromMnemonic() { - seed.resize(64); - zip339_phrase_to_seed(language, mnemonic.c_str(), (uint8_t (*)[64])seed.data()); - } - -public: - MnemonicSeed(Language languageIn, SecureString mnemonicIn): language(languageIn), mnemonic(mnemonicIn) { - SetSeedFromMnemonic(); - } - - /** - * Randomly generate a new mnemonic seed. A SLIP-44 coin type is required to make it possible - * to check that the generated seed can produce valid transparent and unified addresses at account - * numbers 0x7FFFFFFF and 0x00 respectively. - */ - static MnemonicSeed Random(uint32_t bip44CoinType, Language language = English, size_t entropyLen = 32); - - static std::string LanguageName(Language language) { - switch (language) { - case English: - return "English"; - case SimplifiedChinese: - return "Simplified Chinese"; - case TraditionalChinese: - return "Traditional Chinese"; - case Czech: - return "Czech"; - case French: - return "French"; - case Italian: - return "Italian"; - case Japanese: - return "Japanese"; - case Korean: - return "Korean"; - case Portuguese: - return "Portuguese"; - case Spanish: - return "Spanish"; - default: - return "INVALID"; - } - } - - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - if (ser_action.ForRead()) { - uint32_t language0; - - READWRITE(language0); - READWRITE(mnemonic); - language = (Language) language0; - SetSeedFromMnemonic(); - } else { - uint32_t language0 = (uint32_t) language; - - READWRITE(language0); - READWRITE(mnemonic); - } - } - - template - static MnemonicSeed Read(Stream& stream) { - MnemonicSeed seed; - stream >> seed; - return seed; - } - - const Language GetLanguage() const { - return language; - } - - const SecureString& GetMnemonic() const { - return mnemonic; - } - - friend bool operator==(const MnemonicSeed& a, const MnemonicSeed& b) - { - return a.seed == b.seed; - } - - friend bool operator!=(const MnemonicSeed& a, const MnemonicSeed& b) - { - return !(a == b); - } -}; - // This is not part of ZIP 32, but is here because it's linked to the HD seed. uint256 ovkForShieldingFromTaddr(HDSeed& seed); @@ -167,6 +64,13 @@ namespace libzcash { typedef uint32_t AccountId; +/** + * The account identifier used for HD derivation of + * transparent and Sapling addresses via the legacy + * `getnewaddress` and `z_getnewaddress` code paths, + */ +const AccountId ZCASH_LEGACY_ACCOUNT = HARDENED_KEY_LIMIT - 1; + /** * 88-bit diversifier index. This would ideally derive from base_uint * but those values must have bit widths that are multiples of 32. @@ -292,8 +196,8 @@ struct SaplingExtendedSpendingKey { } static SaplingExtendedSpendingKey Master(const HDSeed& seed); - static std::pair ForAccount(const MnemonicSeed& seed, uint32_t bip44CoinType, libzcash::AccountId accountId); - static std::pair Legacy(const MnemonicSeed& seed, uint32_t bip44CoinType, uint32_t addressIndex); + static std::pair ForAccount(const HDSeed& seed, uint32_t bip44CoinType, libzcash::AccountId accountId); + static std::pair Legacy(const HDSeed& seed, uint32_t bip44CoinType, uint32_t addressIndex); SaplingExtendedSpendingKey Derive(uint32_t i) const; @@ -311,104 +215,8 @@ struct SaplingExtendedSpendingKey { } }; -class ZcashdUnifiedSpendingKey; -class ZcashdUnifiedFullViewingKey; - -class ZcashdUnifiedAddress { -private: - diversifier_index_t diversifier_index; - std::optional transparentAddress; - std::optional saplingAddress; - - friend class ZcashdUnifiedFullViewingKey; - - ZcashdUnifiedAddress() {} -public: - const std::optional& GetTransparentAddress() const { - return transparentAddress; - } - - const std::optional& GetSaplingPaymentAddress() const { - return saplingAddress; - } -}; - -class ZcashdUnifiedFullViewingKey { -private: - std::optional transparentKey; - std::optional saplingKey; - - ZcashdUnifiedFullViewingKey() {} - - friend class ZcashdUnifiedSpendingKey; -public: - const std::optional& GetTransparentKey() const { - return transparentKey; - } - - const std::optional& GetSaplingExtendedFullViewingKey() const { - return saplingKey; - } - - std::optional Address(diversifier_index_t j) const; - - std::pair FindAddress(diversifier_index_t j) const { - auto addr = Address(j); - while (!addr.has_value()) { - if (!j.increment()) - throw std::runtime_error(std::string(__func__) + ": diversifier index overflow.");; - addr = Address(j); - } - return std::make_pair(addr.value(), j); - } -}; - -class ZcashdUnifiedSpendingKey { -private: - libzcash::AccountId accountId; - std::optional transparentKey; - std::optional saplingKey; - - ZcashdUnifiedSpendingKey() {} -public: - static std::optional> ForAccount( - const MnemonicSeed& mnemonic, - uint32_t bip44CoinType, - libzcash::AccountId accountId); - - const std::optional& GetTransparentKey() const { - return transparentKey; - } - - const std::optional& GetSaplingExtendedSpendingKey() const { - return saplingKey; - } - - ZcashdUnifiedFullViewingKey ToFullViewingKey() const; -}; - std::optional ParseHDKeypathAccount(uint32_t purpose, uint32_t coinType, const std::string& keyPath); -class Bip44AccountChains { -private: - uint256 seedFp; - libzcash::AccountId accountId; - uint32_t bip44CoinType; - CExtKey external; - CExtKey internal; - - Bip44AccountChains(uint256 seedFpIn, uint32_t bip44CoinTypeIn, libzcash::AccountId accountIdIn, CExtKey externalIn, CExtKey internalIn): - seedFp(seedFpIn), accountId(accountIdIn), bip44CoinType(bip44CoinTypeIn), external(externalIn), internal(internalIn) {} -public: - static std::optional ForAccount( - const MnemonicSeed& mnemonic, - uint32_t bip44CoinType, - libzcash::AccountId accountId); - - std::optional> DeriveExternal(uint32_t addrIndex); - std::optional> DeriveInternal(uint32_t addrIndex); -}; - -} +} //namespace libzcash #endif // ZCASH_ZCASH_ADDRESS_ZIP32_H