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.
This commit is contained in:
Kris Nuttycombe 2021-11-19 14:12:56 -07:00
parent aa808eb07d
commit d65200c860
13 changed files with 442 additions and 364 deletions

View File

@ -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 \

View File

@ -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"

View File

@ -4,6 +4,7 @@
#include <gtest/gtest.h>
#include "zcash/address/mnemonic.h"
#include "zcash/address/orchard.hpp"
TEST(OrchardZkeysTest, IVKSerializationRoundtrip) {

View File

@ -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"

View File

@ -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 <variant>

View File

@ -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<std::pair<CExtKey, HDKeyPath>> 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> 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<std::pair<CExtKey, HDKeyPath>> 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<std::pair<CExtKey, HDKeyPath>> 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);
}

36
src/zcash/address/bip44.h Normal file
View File

@ -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<std::pair<CExtKey, HDKeyPath>> 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<Bip44AccountChains> ForAccount(
const HDSeed& mnemonic,
uint32_t bip44CoinType,
libzcash::AccountId accountId);
std::optional<std::pair<CExtKey, HDKeyPath>> DeriveExternal(uint32_t addrIndex);
std::optional<std::pair<CExtKey, HDKeyPath>> DeriveInternal(uint32_t addrIndex);
};
} //namespace libzcash
#endif // ZCASH_ZCASH_ADDRESS_BIP44_H

View File

@ -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;
}
}
}

View File

@ -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 <typename Stream, typename Operation>
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 <typename Stream>
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

View File

@ -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<std::pair<ZcashdUnifiedSpendingKey, HDKeyPath>> 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<ZcashdUnifiedAddress> 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;
}

View File

@ -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<CKeyID> transparentAddress;
std::optional<SaplingPaymentAddress> saplingAddress;
friend class ZcashdUnifiedFullViewingKey;
ZcashdUnifiedAddress() {}
public:
const std::optional<CKeyID>& GetTransparentAddress() const {
return transparentAddress;
}
const std::optional<SaplingPaymentAddress>& GetSaplingPaymentAddress() const {
return saplingAddress;
}
};
class ZcashdUnifiedFullViewingKey {
private:
std::optional<CExtPubKey> transparentKey;
std::optional<SaplingExtendedFullViewingKey> saplingKey;
ZcashdUnifiedFullViewingKey() {}
friend class ZcashdUnifiedSpendingKey;
public:
const std::optional<CExtPubKey>& GetTransparentKey() const {
return transparentKey;
}
const std::optional<SaplingExtendedFullViewingKey>& GetSaplingExtendedFullViewingKey() const {
return saplingKey;
}
std::optional<ZcashdUnifiedAddress> Address(diversifier_index_t j) const;
std::pair<ZcashdUnifiedAddress, diversifier_index_t> 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<CExtKey> transparentKey;
std::optional<SaplingExtendedSpendingKey> saplingKey;
ZcashdUnifiedSpendingKey() {}
public:
static std::optional<std::pair<ZcashdUnifiedSpendingKey, HDKeyPath>> ForAccount(
const HDSeed& seed,
uint32_t bip44CoinType,
libzcash::AccountId accountId);
const std::optional<CExtKey>& GetTransparentKey() const {
return transparentKey;
}
const std::optional<SaplingExtendedSpendingKey>& GetSaplingExtendedSpendingKey() const {
return saplingKey;
}
ZcashdUnifiedFullViewingKey ToFullViewingKey() const;
};
} //namespace libzcash
#endif // ZCASH_ZCASH_ADDRESS_UNIFIED_H

View File

@ -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<unsigned int> diversifier_index_t::ToTransparentChildIndex() const
}
}
//
// Transparent
//
std::optional<std::pair<CExtKey, HDKeyPath>> 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> 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<std::pair<CExtKey, HDKeyPath>> 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<std::pair<CExtKey, HDKeyPath>> 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, HDKeyPath> SaplingExtendedSpendingKey::ForAccount(const MnemonicSeed& seed, uint32_t bip44CoinType, libzcash::AccountId accountId) {
std::pair<SaplingExtendedSpendingKey, HDKeyPath> 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, HDKeyPath> SaplingExtendedSpendingKey::For
return std::make_pair(xsk, hdKeypath);
}
std::pair<SaplingExtendedSpendingKey, HDKeyPath> SaplingExtendedSpendingKey::Legacy(const MnemonicSeed& seed, uint32_t bip44CoinType, uint32_t addressIndex) {
std::pair<SaplingExtendedSpendingKey, HDKeyPath> 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<std::pair<ZcashdUnifiedSpendingKey, HDKeyPath>> 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<ZcashdUnifiedAddress> 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<unsigned long> 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;

View File

@ -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<unsigned char, secure_allocator<unsigned char>> 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 <typename Stream, typename Operation>
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 <typename Stream>
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<SaplingExtendedSpendingKey, HDKeyPath> ForAccount(const MnemonicSeed& seed, uint32_t bip44CoinType, libzcash::AccountId accountId);
static std::pair<SaplingExtendedSpendingKey, HDKeyPath> Legacy(const MnemonicSeed& seed, uint32_t bip44CoinType, uint32_t addressIndex);
static std::pair<SaplingExtendedSpendingKey, HDKeyPath> ForAccount(const HDSeed& seed, uint32_t bip44CoinType, libzcash::AccountId accountId);
static std::pair<SaplingExtendedSpendingKey, HDKeyPath> 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<CKeyID> transparentAddress;
std::optional<SaplingPaymentAddress> saplingAddress;
friend class ZcashdUnifiedFullViewingKey;
ZcashdUnifiedAddress() {}
public:
const std::optional<CKeyID>& GetTransparentAddress() const {
return transparentAddress;
}
const std::optional<SaplingPaymentAddress>& GetSaplingPaymentAddress() const {
return saplingAddress;
}
};
class ZcashdUnifiedFullViewingKey {
private:
std::optional<CExtPubKey> transparentKey;
std::optional<SaplingExtendedFullViewingKey> saplingKey;
ZcashdUnifiedFullViewingKey() {}
friend class ZcashdUnifiedSpendingKey;
public:
const std::optional<CExtPubKey>& GetTransparentKey() const {
return transparentKey;
}
const std::optional<SaplingExtendedFullViewingKey>& GetSaplingExtendedFullViewingKey() const {
return saplingKey;
}
std::optional<ZcashdUnifiedAddress> Address(diversifier_index_t j) const;
std::pair<ZcashdUnifiedAddress, diversifier_index_t> 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<CExtKey> transparentKey;
std::optional<SaplingExtendedSpendingKey> saplingKey;
ZcashdUnifiedSpendingKey() {}
public:
static std::optional<std::pair<ZcashdUnifiedSpendingKey, HDKeyPath>> ForAccount(
const MnemonicSeed& mnemonic,
uint32_t bip44CoinType,
libzcash::AccountId accountId);
const std::optional<CExtKey>& GetTransparentKey() const {
return transparentKey;
}
const std::optional<SaplingExtendedSpendingKey>& GetSaplingExtendedSpendingKey() const {
return saplingKey;
}
ZcashdUnifiedFullViewingKey ToFullViewingKey() const;
};
std::optional<unsigned long> 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<Bip44AccountChains> ForAccount(
const MnemonicSeed& mnemonic,
uint32_t bip44CoinType,
libzcash::AccountId accountId);
std::optional<std::pair<CExtKey, HDKeyPath>> DeriveExternal(uint32_t addrIndex);
std::optional<std::pair<CExtKey, HDKeyPath>> DeriveInternal(uint32_t addrIndex);
};
}
} //namespace libzcash
#endif // ZCASH_ZCASH_ADDRESS_ZIP32_H