Add a first-class type for transparent full viewing keys.
This commit is contained in:
parent
c21ffff790
commit
bdcb12e445
|
@ -624,6 +624,17 @@ dependencies = [
|
|||
"ahash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hdwallet"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/nuttycom/hdwallet?rev=576683b9f2865f1118c309017ff36e01f84420c9#576683b9f2865f1118c309017ff36e01f84420c9"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"rand_core 0.6.3",
|
||||
"ring",
|
||||
"secp256k1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
|
@ -1404,6 +1415,32 @@ version = "0.6.25"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.16.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"spin",
|
||||
"untrusted",
|
||||
"web-sys",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ripemd160"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2eca4ecc81b7f313189bf73ce724400a07da2a6dac19588b03c8bd76a2dcc251"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"digest",
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
|
@ -1493,6 +1530,12 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
|
@ -1715,6 +1758,12 @@ dependencies = [
|
|||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.3"
|
||||
|
@ -1890,12 +1939,14 @@ dependencies = [
|
|||
"blake2b_simd 1.0.0",
|
||||
"blake2s_simd 1.0.0",
|
||||
"bls12_381",
|
||||
"bs58",
|
||||
"byteorder",
|
||||
"chacha20poly1305",
|
||||
"equihash",
|
||||
"ff",
|
||||
"fpe",
|
||||
"group",
|
||||
"hdwallet",
|
||||
"hex",
|
||||
"incrementalmerkletree",
|
||||
"jubjub",
|
||||
|
@ -1905,6 +1956,8 @@ dependencies = [
|
|||
"orchard",
|
||||
"rand",
|
||||
"rand_core 0.6.3",
|
||||
"ripemd160",
|
||||
"secp256k1",
|
||||
"sha2",
|
||||
"subtle",
|
||||
"zcash_encoding",
|
||||
|
|
|
@ -47,7 +47,7 @@ tracing-appender = "0.2"
|
|||
zcash_address = "0.0"
|
||||
zcash_history = "0.2"
|
||||
zcash_note_encryption = "0.1"
|
||||
zcash_primitives = "0.5"
|
||||
zcash_primitives = { version = "0.5", features = ["transparent-inputs"] }
|
||||
zcash_proofs = "0.5"
|
||||
ed25519-zebra = "3"
|
||||
zeroize = "1.4.2"
|
||||
|
@ -71,6 +71,7 @@ panic = 'abort'
|
|||
codegen-units = 1
|
||||
|
||||
[patch.crates-io]
|
||||
hdwallet = { git = "https://github.com/nuttycom/hdwallet", rev = "576683b9f2865f1118c309017ff36e01f84420c9" }
|
||||
zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "a01290869a4f0c4e05bdcca550795ea0e76e8550" }
|
||||
zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "a01290869a4f0c4e05bdcca550795ea0e76e8550" }
|
||||
zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "a01290869a4f0c4e05bdcca550795ea0e76e8550" }
|
||||
|
|
|
@ -114,7 +114,7 @@ LIBZCASH_H = \
|
|||
zcash/IncrementalMerkleTree.hpp \
|
||||
zcash/NoteEncryption.hpp \
|
||||
zcash/Address.hpp \
|
||||
zcash/address/bip44.h \
|
||||
zcash/address/transparent.h \
|
||||
zcash/address/mnemonic.h \
|
||||
zcash/address/orchard.h \
|
||||
zcash/address/sapling.hpp \
|
||||
|
@ -547,7 +547,7 @@ libzcash_a_SOURCES = \
|
|||
zcash/IncrementalMerkleTree.cpp \
|
||||
zcash/NoteEncryption.cpp \
|
||||
zcash/Address.cpp \
|
||||
zcash/address/bip44.cpp \
|
||||
zcash/address/transparent.cpp \
|
||||
zcash/address/mnemonic.cpp \
|
||||
zcash/address/orchard.cpp \
|
||||
zcash/address/sapling.cpp \
|
||||
|
|
|
@ -98,6 +98,16 @@ bool CPubKey::Derive(CPubKey& pubkeyChild, ChainCode &ccChild, unsigned int nChi
|
|||
return true;
|
||||
}
|
||||
|
||||
std::optional<CChainablePubKey> CChainablePubKey::Derive(unsigned int nChild) const {
|
||||
CPubKey pubkeyChild;
|
||||
ChainCode ccChild;
|
||||
if (pubkey.Derive(pubkeyChild, ccChild, nChild, chaincode)) {
|
||||
return CChainablePubKey::FromParts(ccChild, pubkeyChild);
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
void CExtPubKey::Encode(unsigned char code[BIP32_EXTKEY_SIZE]) const {
|
||||
code[0] = nDepth;
|
||||
memcpy(code+1, vchFingerprint, 4);
|
||||
|
|
|
@ -221,6 +221,8 @@ public:
|
|||
return pubkey;
|
||||
}
|
||||
|
||||
std::optional<CChainablePubKey> Derive(unsigned int nChild) const;
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
|
@ -272,6 +274,10 @@ struct CExtPubKey {
|
|||
void Decode(const unsigned char code[BIP32_EXTKEY_SIZE]);
|
||||
bool Derive(CExtPubKey& out, unsigned int nChild) const;
|
||||
|
||||
std::optional<CChainablePubKey> ToChainablePubKey() const {
|
||||
return CChainablePubKey::FromParts(chaincode, pubkey);
|
||||
}
|
||||
|
||||
void Serialize(CSizeComputer& s) const
|
||||
{
|
||||
// Optimized implementation for ::GetSerializeSize that avoids copying.
|
||||
|
|
|
@ -3,6 +3,7 @@ use std::ffi::{CStr, CString};
|
|||
use tracing::error;
|
||||
|
||||
use zcash_address::unified::{Container, Encoding, Fvk, Ufvk};
|
||||
use zcash_primitives::legacy::keys::AccountPubKey;
|
||||
|
||||
use crate::address_ffi::network_from_cstr;
|
||||
|
||||
|
@ -165,3 +166,29 @@ pub extern "C" fn unified_full_viewing_key_from_components(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn transparent_key_ovks(
|
||||
t_key: *const [u8; 65],
|
||||
internal_ovk_ret: *mut [u8; 32],
|
||||
external_ovk_ret: *mut [u8; 32],
|
||||
) -> bool {
|
||||
let key_bytes = unsafe { t_key.as_ref() }.expect("Transparent FVK pointer may not be null.");
|
||||
let internal_ovk = unsafe { &mut *internal_ovk_ret };
|
||||
let external_ovk = unsafe { &mut *external_ovk_ret };
|
||||
match AccountPubKey::deserialize(key_bytes) {
|
||||
Ok(epubkey) => {
|
||||
let (internal, external) = epubkey.ovks_for_shielding();
|
||||
*internal_ovk = internal.as_bytes();
|
||||
*external_ovk = external.as_bytes();
|
||||
true
|
||||
}
|
||||
Err(e) => {
|
||||
error!(
|
||||
"An error occurred parsing the transparent full viewing key: {:?}",
|
||||
e
|
||||
);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -276,26 +276,29 @@ CPubKey CWallet::GenerateNewKey()
|
|||
// All mnemonic seeds are checked at construction to ensure that we can obtain
|
||||
// a valid spending key for the account ZCASH_LEGACY_ACCOUNT;
|
||||
// therefore, the `value()` call here is safe.
|
||||
Bip44AccountChains accountChains = Bip44AccountChains::ForAccount(
|
||||
transparent::AccountKey accountKey = transparent::AccountKey::ForAccount(
|
||||
seed,
|
||||
BIP44CoinType(),
|
||||
ZCASH_LEGACY_ACCOUNT).value();
|
||||
|
||||
std::optional<std::pair<CKey, HDKeyPath>> extKey = std::nullopt;
|
||||
std::optional<CPubKey> pubkey = std::nullopt;
|
||||
do {
|
||||
extKey = accountChains.DeriveExternal(hdChain.GetLegacyTKeyCounter());
|
||||
auto index = hdChain.GetLegacyTKeyCounter();
|
||||
auto key = accountKey.DeriveExternalSpendingKey(index);
|
||||
hdChain.IncrementLegacyTKeyCounter();
|
||||
if (key.has_value()) {
|
||||
auto keyPath = transparent::AccountKey::KeyPath(BIP44CoinType(), ZCASH_LEGACY_ACCOUNT, true, index);
|
||||
pubkey = AddTransparentSecretKey(seed.Fingerprint(), std::make_pair(key.value(), keyPath));
|
||||
}
|
||||
// if we did not successfully generate a key, try again.
|
||||
} while (!extKey.has_value());
|
||||
|
||||
auto pubkey = AddTransparentSecretKey(seed.Fingerprint(), extKey.value());
|
||||
} while (!pubkey.has_value());
|
||||
|
||||
// Update the persisted chain information
|
||||
if (fFileBacked && !CWalletDB(strWalletFile).WriteMnemonicHDChain(hdChain)) {
|
||||
throw std::runtime_error("CWallet::GenerateNewKey(): Writing HD chain model failed");
|
||||
}
|
||||
|
||||
return pubkey;
|
||||
return pubkey.value();
|
||||
}
|
||||
|
||||
CPubKey CWallet::AddTransparentSecretKey(
|
||||
|
@ -479,14 +482,9 @@ std::optional<libzcash::ZcashdUnifiedSpendingKey>
|
|||
// LoadUnifiedAccountMetadata().
|
||||
mapUfvkAddressMetadata.insert({ufvkid, UFVKAddressMetadata(accountId)});
|
||||
|
||||
// Add Transparent component to the wallet
|
||||
AddTransparentSecretKey(
|
||||
skmeta.GetSeedFingerprint(),
|
||||
std::make_pair(
|
||||
usk.value().GetTransparentKey().key,
|
||||
libzcash::Bip44TransparentAccountKeyPath(BIP44CoinType(), accountId)
|
||||
)
|
||||
);
|
||||
// We do not explicitly add any transparent component to the keystore;
|
||||
// the secret keys that we need to store are the child spending keys
|
||||
// that are produced whenever we create a transparent address.
|
||||
|
||||
// Add Sapling component to the wallet
|
||||
auto saplingEsk = usk.value().GetSaplingExtendedSpendingKey();
|
||||
|
@ -658,9 +656,21 @@ UAGenerationResult CWallet::GenerateUnifiedAddress(
|
|||
// Regenerate the secret key for the transparent address and add it to
|
||||
// the wallet.
|
||||
auto seed = GetMnemonicSeed().value();
|
||||
auto b44 = libzcash::Bip44AccountChains::ForAccount(seed, BIP44CoinType(), accountId).value();
|
||||
auto key = b44.DeriveExternal(diversifierIndex.ToTransparentChildIndex().value()).value();
|
||||
AddTransparentSecretKey(seed.Fingerprint(), key);
|
||||
auto accountKey = transparent::AccountKey::ForAccount(seed, BIP44CoinType(), accountId).value();
|
||||
// this .value is known to be safe from the earlier check
|
||||
auto childIndex = diversifierIndex.ToTransparentChildIndex().value();
|
||||
auto key = accountKey.DeriveExternalSpendingKey(childIndex).value();
|
||||
|
||||
AddTransparentSecretKey(
|
||||
seed.Fingerprint(),
|
||||
std::make_pair(
|
||||
key,
|
||||
transparent::AccountKey::KeyPath(BIP44CoinType(), accountId, true, childIndex)
|
||||
)
|
||||
);
|
||||
|
||||
// We do not add the change address for the transparent key, because
|
||||
// we do not send transparent change when using unified accounts.
|
||||
}
|
||||
|
||||
return std::make_pair(address.value(), diversifierIndex);
|
||||
|
@ -1555,7 +1565,7 @@ std::optional<RecipientAddress> CWallet::GenerateChangeAddressForAccount(
|
|||
if (t == libzcash::ChangeType::Transparent && accountId == ZCASH_LEGACY_ACCOUNT) {
|
||||
return GenerateNewKey().GetID();
|
||||
} else {
|
||||
return ufvk.value().GetChangeAddress(changeOptions);
|
||||
return ufvk.value().GetChangeAddress(SaplingChangeRequest());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
#include "wallet/crypter.h"
|
||||
#include "wallet/walletdb.h"
|
||||
#include "wallet/rpcwallet.h"
|
||||
#include "zcash/address/bip44.h"
|
||||
#include "zcash/address/unified.h"
|
||||
#include "zcash/address/mnemonic.h"
|
||||
#include "zcash/Address.hpp"
|
||||
|
|
|
@ -195,10 +195,10 @@ std::optional<CChainablePubKey> libzcash::UnifiedFullViewingKey::GetTransparentK
|
|||
}
|
||||
}
|
||||
|
||||
bool libzcash::UnifiedFullViewingKeyBuilder::AddTransparentKey(const CChainablePubKey& key) {
|
||||
bool libzcash::UnifiedFullViewingKeyBuilder::AddTransparentKey(const transparent::AccountPubKey& key) {
|
||||
if (t_bytes.has_value()) return false;
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << key;
|
||||
ss << key.GetPubKey();
|
||||
assert(ss.size() == 65);
|
||||
std::vector<uint8_t> ss_bytes(ss.begin(), ss.end());
|
||||
t_bytes = ss_bytes;
|
||||
|
|
|
@ -201,7 +201,7 @@ private:
|
|||
public:
|
||||
UnifiedFullViewingKeyBuilder(): t_bytes(std::nullopt), sapling_bytes(std::nullopt) {}
|
||||
|
||||
bool AddTransparentKey(const CChainablePubKey&);
|
||||
bool AddTransparentKey(const transparent::AccountPubKey&);
|
||||
bool AddSaplingKey(const SaplingDiversifiableFullViewingKey&);
|
||||
|
||||
std::optional<UnifiedFullViewingKey> build() const;
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
// 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"
|
||||
|
||||
HDKeyPath libzcash::Bip44TransparentAccountKeyPath(uint32_t bip44CoinType, libzcash::AccountId accountId) {
|
||||
return "m/44'/" + std::to_string(bip44CoinType) + "'/" + std::to_string(accountId) + "'";
|
||||
}
|
||||
|
||||
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 = libzcash::Bip44TransparentAccountKeyPath(bip44CoinType, 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<CKey, HDKeyPath>> libzcash::Bip44AccountChains::DeriveExternal(uint32_t addrIndex) {
|
||||
auto childKey = external.Derive(addrIndex);
|
||||
if (!childKey.has_value()) return std::nullopt;
|
||||
|
||||
auto hdKeypath = Bip44TransparentAccountKeyPath(bip44CoinType, accountId) + "/0/" + std::to_string(addrIndex);
|
||||
|
||||
return std::make_pair(childKey.value().key, hdKeypath);
|
||||
}
|
||||
|
||||
std::optional<std::pair<CKey, HDKeyPath>> libzcash::Bip44AccountChains::DeriveInternal(uint32_t addrIndex) {
|
||||
auto childKey = internal.Derive(addrIndex);
|
||||
if (!childKey.has_value()) return std::nullopt;
|
||||
|
||||
auto hdKeypath = Bip44TransparentAccountKeyPath(bip44CoinType, accountId) + "/1/" + std::to_string(addrIndex);
|
||||
|
||||
return std::make_pair(childKey.value().key, hdKeypath);
|
||||
}
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
// 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 {
|
||||
|
||||
HDKeyPath Bip44TransparentAccountKeyPath(uint32_t bip44CoinType, libzcash::AccountId accountId);
|
||||
|
||||
/**
|
||||
* Derive a transparent extended key in compressed format for the specified
|
||||
* seed, bip44 coin type, and account ID.
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* Generate the key corresponding to the specified index at the "external child"
|
||||
* level of the BIP44 path for the account.
|
||||
*/
|
||||
std::optional<std::pair<CKey, HDKeyPath>> DeriveExternal(uint32_t addrIndex);
|
||||
|
||||
/**
|
||||
* Generate the key corresponding to the specified index at the "internal child"
|
||||
* level of the BIP44 path for the account. This should probably only usually be
|
||||
* used at address index 0, but ordinarily it won't need to be used at all since
|
||||
* all change should be shielded by default.
|
||||
*/
|
||||
std::optional<std::pair<CKey, HDKeyPath>> DeriveInternal(uint32_t addrIndex = 0);
|
||||
};
|
||||
|
||||
} //namespace libzcash
|
||||
|
||||
#endif // ZCASH_ZCASH_ADDRESS_BIP44_H
|
|
@ -5,8 +5,6 @@
|
|||
#include "random.h"
|
||||
|
||||
#include "mnemonic.h"
|
||||
|
||||
#include "bip44.h"
|
||||
#include "unified.h"
|
||||
|
||||
using namespace libzcash;
|
||||
|
@ -33,7 +31,7 @@ MnemonicSeed MnemonicSeed::Random(uint32_t bip44CoinType, Language language, siz
|
|||
// 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()) {
|
||||
transparent::AccountKey::ForAccount(seed, bip44CoinType, ZCASH_LEGACY_ACCOUNT).has_value()) {
|
||||
return seed;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
// 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 "transparent.h"
|
||||
|
||||
namespace libzcash {
|
||||
namespace transparent {
|
||||
|
||||
std::optional<CPubKey> AccountPubKey::DeriveExternal(uint32_t addrIndex) const {
|
||||
auto externalKey = pubkey.Derive(0);
|
||||
if (!externalKey.has_value()) return std::nullopt;
|
||||
auto childKey = externalKey.value().Derive(addrIndex);
|
||||
if (!childKey.has_value()) return std::nullopt;
|
||||
return childKey.value().GetPubKey();
|
||||
}
|
||||
|
||||
std::optional<CPubKey> AccountPubKey::DeriveInternal(uint32_t addrIndex) const {
|
||||
auto internalKey = pubkey.Derive(1);
|
||||
if (!internalKey.has_value()) return std::nullopt;
|
||||
auto childKey = internalKey.value().Derive(addrIndex);
|
||||
if (!childKey.has_value()) return std::nullopt;
|
||||
return childKey.value().GetPubKey();
|
||||
}
|
||||
|
||||
std::optional<CKeyID> AccountPubKey::GetChangeAddress(const diversifier_index_t& j) const {
|
||||
auto childIndex = j.ToTransparentChildIndex();
|
||||
if (!childIndex.has_value()) return std::nullopt;
|
||||
|
||||
auto changeKey = this->DeriveInternal(childIndex.value());
|
||||
if (!changeKey.has_value()) return std::nullopt;
|
||||
|
||||
return changeKey.value().GetID();
|
||||
}
|
||||
|
||||
std::optional<std::pair<CKeyID, diversifier_index_t>> AccountPubKey::FindChangeAddress(diversifier_index_t j) const {
|
||||
while (true) {
|
||||
auto childIndex = j.ToTransparentChildIndex();
|
||||
if (!childIndex.has_value()) return std::nullopt;
|
||||
|
||||
auto changeKey = this->DeriveInternal(childIndex.value());
|
||||
if (changeKey.has_value()) {
|
||||
return std::make_pair(changeKey.value().GetID(), j);
|
||||
} else {
|
||||
j.increment();
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<AccountKey> AccountKey::ForAccount(
|
||||
const HDSeed& seed,
|
||||
uint32_t bip44CoinType,
|
||||
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 accountKeyOpt = m_44h_cth.value().Derive(accountId | HARDENED_KEY_LIMIT);
|
||||
if (!accountKeyOpt.has_value()) return std::nullopt;
|
||||
|
||||
auto accountKey = accountKeyOpt.value();
|
||||
auto external = accountKey.Derive(0);
|
||||
auto internal = accountKey.Derive(1);
|
||||
|
||||
if (!(external.has_value() && internal.has_value())) return std::nullopt;
|
||||
|
||||
return AccountKey(accountKey, external.value(), internal.value());
|
||||
}
|
||||
|
||||
std::optional<CKey> AccountKey::DeriveExternalSpendingKey(uint32_t addrIndex) const {
|
||||
auto childKey = external.Derive(addrIndex);
|
||||
if (!childKey.has_value()) return std::nullopt;
|
||||
|
||||
return childKey.value().key;
|
||||
}
|
||||
|
||||
std::optional<CKey> AccountKey::DeriveInternalSpendingKey(uint32_t addrIndex) const {
|
||||
auto childKey = internal.Derive(addrIndex);
|
||||
if (!childKey.has_value()) return std::nullopt;
|
||||
|
||||
return childKey.value().key;
|
||||
}
|
||||
|
||||
AccountPubKey AccountKey::ToAccountPubKey() const {
|
||||
// The .value() call is safe here because we never derive
|
||||
// non-compressed public keys.
|
||||
return accountKey.Neuter().ToChainablePubKey().value();
|
||||
}
|
||||
|
||||
} //namespace transparent
|
||||
} //namespace libzcash
|
|
@ -0,0 +1,106 @@
|
|||
// 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_TRANSPARENT_H
|
||||
#define ZCASH_ZCASH_ADDRESS_TRANSPARENT_H
|
||||
|
||||
#include "zip32.h"
|
||||
|
||||
namespace libzcash {
|
||||
namespace transparent {
|
||||
|
||||
class AccountPubKey {
|
||||
private:
|
||||
CChainablePubKey pubkey;
|
||||
public:
|
||||
AccountPubKey(CChainablePubKey pubkey): pubkey(pubkey) {};
|
||||
|
||||
const CChainablePubKey& GetPubKey() const {
|
||||
return pubkey;
|
||||
}
|
||||
|
||||
std::optional<CPubKey> DeriveExternal(uint32_t addrIndex) const;
|
||||
|
||||
std::optional<CPubKey> DeriveInternal(uint32_t addrIndex) const;
|
||||
|
||||
/**
|
||||
* Return the transparent change address for this key a the given diversifier
|
||||
* index, if that index is valid for a transparent address and if key
|
||||
* derivation is successful.
|
||||
*/
|
||||
std::optional<CKeyID> GetChangeAddress(const diversifier_index_t& j) const;
|
||||
|
||||
/**
|
||||
* Search the space of valid child indices starting at the given index, and
|
||||
* return the change address corresponding to the first index at which a
|
||||
* valid address is derived, along with that index. This will always return
|
||||
* a valid address unless the space of valid transparent child indices is
|
||||
* exhausted or the provided diversifier index exceeds the maximum allowed
|
||||
* non-hardened transparent child index.
|
||||
*/
|
||||
std::optional<std::pair<CKeyID, diversifier_index_t>> FindChangeAddress(diversifier_index_t j) const;
|
||||
|
||||
friend bool operator==(const AccountPubKey& a, const AccountPubKey& b)
|
||||
{
|
||||
return a.pubkey == b.pubkey;
|
||||
}
|
||||
};
|
||||
|
||||
class AccountKey {
|
||||
private:
|
||||
CExtKey accountKey;
|
||||
CExtKey external;
|
||||
CExtKey internal;
|
||||
|
||||
AccountKey(CExtKey accountKeyIn, CExtKey externalIn, CExtKey internalIn):
|
||||
accountKey(accountKeyIn), external(externalIn), internal(internalIn) {}
|
||||
public:
|
||||
static std::optional<AccountKey> ForAccount(
|
||||
const HDSeed& mnemonic,
|
||||
uint32_t bip44CoinType,
|
||||
libzcash::AccountId accountId);
|
||||
|
||||
static HDKeyPath KeyPath(uint32_t bip44CoinType, libzcash::AccountId accountId) {
|
||||
return
|
||||
"m/44'/" +
|
||||
std::to_string(bip44CoinType) + "'/" +
|
||||
std::to_string(accountId) + "'";
|
||||
}
|
||||
|
||||
static HDKeyPath KeyPath(uint32_t bip44CoinType, libzcash::AccountId accountId, bool external, uint32_t childIndex) {
|
||||
return
|
||||
AccountKey::KeyPath(bip44CoinType, accountId) + "/" +
|
||||
(external ? "0/" : "1/") +
|
||||
std::to_string(childIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the key corresponding to the specified index at the "external child"
|
||||
* level of the TRANSPARENT path for the account.
|
||||
*/
|
||||
std::optional<CKey> DeriveExternalSpendingKey(uint32_t addrIndex) const;
|
||||
|
||||
/**
|
||||
* Generate the key corresponding to the specified index at the "internal child"
|
||||
* level of the TRANSPARENT path for the account. This should probably only usually be
|
||||
* used at address index 0, but ordinarily it won't need to be used at all since
|
||||
* all change should be shielded by default.
|
||||
*/
|
||||
std::optional<CKey> DeriveInternalSpendingKey(uint32_t addrIndex = 0) const;
|
||||
|
||||
/**
|
||||
* Return the public key associated with this spending key.
|
||||
*/
|
||||
AccountPubKey ToAccountPubKey() const;
|
||||
|
||||
friend bool operator==(const AccountKey& a, const AccountKey& b)
|
||||
{
|
||||
return a.accountKey == b.accountKey;
|
||||
}
|
||||
};
|
||||
|
||||
} //namespace transparent
|
||||
} //namespace libzcash
|
||||
|
||||
#endif // ZCASH_ZCASH_ADDRESS_TRANSPARENT_H
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
#include "zcash/Address.hpp"
|
||||
#include "unified.h"
|
||||
#include "bip44.h"
|
||||
|
||||
using namespace libzcash;
|
||||
|
||||
|
@ -32,23 +31,19 @@ std::optional<ZcashdUnifiedSpendingKey> ZcashdUnifiedSpendingKey::ForAccount(
|
|||
const HDSeed& seed,
|
||||
uint32_t bip44CoinType,
|
||||
AccountId accountId) {
|
||||
ZcashdUnifiedSpendingKey usk;
|
||||
|
||||
auto transparentKey = DeriveBip44TransparentAccountKey(seed, bip44CoinType, accountId);
|
||||
auto transparentKey = transparent::AccountKey::ForAccount(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 usk;
|
||||
return ZcashdUnifiedSpendingKey(transparentKey.value(), saplingKey.first);
|
||||
}
|
||||
|
||||
UnifiedFullViewingKey ZcashdUnifiedSpendingKey::ToFullViewingKey() const {
|
||||
UnifiedFullViewingKeyBuilder builder;
|
||||
|
||||
auto extPubKey = transparentKey.Neuter();
|
||||
builder.AddTransparentKey(CChainablePubKey::FromParts(extPubKey.chaincode, extPubKey.pubkey).value());
|
||||
builder.AddTransparentKey(transparentKey.ToAccountPubKey());
|
||||
builder.AddSaplingKey(saplingKey.ToXFVK());
|
||||
|
||||
// This call to .value() is safe as ZcashdUnifiedSpendingKey values are always
|
||||
|
@ -95,22 +90,14 @@ std::optional<UnifiedAddress> ZcashdUnifiedFullViewingKey::Address(
|
|||
|
||||
if (transparentKey.has_value() && receiverTypes.count(ReceiverType::P2PKH) > 0) {
|
||||
const auto& tkey = transparentKey.value();
|
||||
|
||||
auto childIndex = j.ToTransparentChildIndex();
|
||||
if (!childIndex.has_value()) return std::nullopt;
|
||||
|
||||
CPubKey externalKey;
|
||||
ChainCode externalChainCode;
|
||||
if (!tkey.GetPubKey().Derive(externalKey, externalChainCode, 0, tkey.GetChainCode())) {
|
||||
return std::nullopt;
|
||||
}
|
||||
auto externalPubkey = tkey.DeriveExternal(childIndex.value());
|
||||
if (!externalPubkey.has_value()) return std::nullopt;
|
||||
|
||||
CPubKey childKey;
|
||||
ChainCode childChainCode;
|
||||
if (externalKey.Derive(childKey, childChainCode, childIndex.value(), externalChainCode)) {
|
||||
ua.AddReceiver(childKey.GetID());
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
ua.AddReceiver(externalPubkey.value().GetID());
|
||||
}
|
||||
|
||||
return ua;
|
||||
|
@ -135,6 +122,57 @@ std::optional<std::pair<UnifiedAddress, diversifier_index_t>> ZcashdUnifiedFullV
|
|||
return FindAddress(j, {ReceiverType::P2PKH, ReceiverType::Sapling});
|
||||
}
|
||||
|
||||
RecipientAddress ZcashdUnifiedFullViewingKey::GetChangeAddress(const std::set<ChangeType>& changeOptions) const {
|
||||
throw std::runtime_error("TODO");
|
||||
std::optional<RecipientAddress> ZcashdUnifiedFullViewingKey::GetChangeAddress(const ChangeRequest& req) const {
|
||||
std::optional<RecipientAddress> addr;
|
||||
std::visit(match {
|
||||
[&](const TransparentChangeRequest& req) {
|
||||
auto changeKey = this->GetTransparentChangeAddress(req.GetIndex());
|
||||
if (changeKey.has_value()) {
|
||||
addr = changeKey.value();
|
||||
}
|
||||
},
|
||||
[&](const SaplingChangeRequest& req) {
|
||||
std::optional<RecipientAddress> addr;
|
||||
// currently true by construction, as a UFVK must have a shielded component
|
||||
if (saplingKey.has_value()) {
|
||||
addr = saplingKey.value().GetChangeAddress();
|
||||
}
|
||||
}
|
||||
}, req);
|
||||
return addr;
|
||||
}
|
||||
|
||||
std::optional<CKeyID> ZcashdUnifiedFullViewingKey::GetTransparentChangeAddress(const diversifier_index_t& j) const {
|
||||
if (transparentKey.has_value()) {
|
||||
auto childIndex = j.ToTransparentChildIndex();
|
||||
if (!childIndex.has_value()) return std::nullopt;
|
||||
|
||||
auto changeKey = transparentKey.value().DeriveInternal(childIndex.value());
|
||||
if (!changeKey.has_value()) return std::nullopt;
|
||||
|
||||
return changeKey.value().GetID();
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
//std::optional<std::pair<uint256, uint256>> ZcashdUnifiedFullViewingKey::GetTransparentOVKsForShielding() const {
|
||||
// if (transparentKey.has_value()) {
|
||||
// CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
// ss << transparentKey.value().GetPubKey();
|
||||
// assert(ss.size() == 65);
|
||||
// CSerializeData ss_bytes(ss.begin(), ss.end());
|
||||
//
|
||||
// uint256 internalOVK;
|
||||
// uint256 externalOVK;
|
||||
//
|
||||
// assert(transparent_key_ovks(
|
||||
// reinterpret_cast<unsigned char*>(ss_bytes.data()),
|
||||
// internalOVK.begin(),
|
||||
// externalOVK.begin()));
|
||||
//
|
||||
// return std::make_pair(internalOVK, externalOVK);
|
||||
// } else {
|
||||
// return std::nullopt;
|
||||
// }
|
||||
//}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#ifndef ZCASH_ZCASH_ADDRESS_UNIFIED_H
|
||||
#define ZCASH_ZCASH_ADDRESS_UNIFIED_H
|
||||
|
||||
#include "bip44.h"
|
||||
#include "transparent.h"
|
||||
#include "key_constants.h"
|
||||
#include "script/script.h"
|
||||
#include "zip32.h"
|
||||
|
@ -26,14 +26,32 @@ typedef std::variant<
|
|||
libzcash::SaplingPaymentAddress> RecipientAddress;
|
||||
|
||||
/**
|
||||
* An enumeration of the types of change that a transaction may
|
||||
* produce.
|
||||
* An enumeration of the types of change that a transaction may produce. It is
|
||||
* sorted in descending preference order, so that when iterating over a set of
|
||||
* change types the most-preferred type is selected first.
|
||||
*/
|
||||
enum class ChangeType {
|
||||
Sapling,
|
||||
Transparent,
|
||||
};
|
||||
|
||||
class TransparentChangeRequest {
|
||||
private:
|
||||
const diversifier_index_t& index;
|
||||
public:
|
||||
TransparentChangeRequest(const diversifier_index_t& indexIn): index(indexIn) {}
|
||||
|
||||
const diversifier_index_t& GetIndex() const {
|
||||
return index;
|
||||
}
|
||||
};
|
||||
|
||||
class SaplingChangeRequest {};
|
||||
|
||||
typedef std::variant<
|
||||
TransparentChangeRequest,
|
||||
SaplingChangeRequest> ChangeRequest;
|
||||
|
||||
/**
|
||||
* Test whether the specified list of receiver types contains a
|
||||
* shielded receiver type
|
||||
|
@ -105,7 +123,7 @@ public:
|
|||
class ZcashdUnifiedFullViewingKey {
|
||||
private:
|
||||
UFVKId keyId;
|
||||
std::optional<CChainablePubKey> transparentKey;
|
||||
std::optional<transparent::AccountPubKey> transparentKey;
|
||||
std::optional<SaplingDiversifiableFullViewingKey> saplingKey;
|
||||
|
||||
ZcashdUnifiedFullViewingKey() {}
|
||||
|
@ -124,7 +142,10 @@ public:
|
|||
return keyId;
|
||||
}
|
||||
|
||||
const std::optional<CChainablePubKey>& GetTransparentKey() const {
|
||||
/**
|
||||
* Return the transparent key at the account level;
|
||||
*/
|
||||
const std::optional<transparent::AccountPubKey>& GetTransparentKey() const {
|
||||
return transparentKey;
|
||||
}
|
||||
|
||||
|
@ -172,7 +193,14 @@ public:
|
|||
* If the provided set is empty, return the change address
|
||||
* corresponding to the most-preferred pool.
|
||||
*/
|
||||
RecipientAddress GetChangeAddress(const std::set<ChangeType>& changeOptions) const;
|
||||
std::optional<RecipientAddress> GetChangeAddress(const ChangeRequest& req) const;
|
||||
|
||||
/**
|
||||
* Return the transparent change address for this UFVK a the given diversifier
|
||||
* index, if the UFVK has a transparent component and it is possible to derive
|
||||
* an address at this index.
|
||||
*/
|
||||
std::optional<CKeyID> GetTransparentChangeAddress(const diversifier_index_t& j) const;
|
||||
|
||||
friend bool operator==(const ZcashdUnifiedFullViewingKey& a, const ZcashdUnifiedFullViewingKey& b)
|
||||
{
|
||||
|
@ -185,17 +213,19 @@ public:
|
|||
*/
|
||||
class ZcashdUnifiedSpendingKey {
|
||||
private:
|
||||
CExtKey transparentKey;
|
||||
transparent::AccountKey transparentKey;
|
||||
SaplingExtendedSpendingKey saplingKey;
|
||||
|
||||
ZcashdUnifiedSpendingKey() {}
|
||||
ZcashdUnifiedSpendingKey(
|
||||
transparent::AccountKey tkey,
|
||||
SaplingExtendedSpendingKey skey): transparentKey(tkey), saplingKey(skey) {}
|
||||
public:
|
||||
static std::optional<ZcashdUnifiedSpendingKey> ForAccount(
|
||||
const HDSeed& seed,
|
||||
uint32_t bip44CoinType,
|
||||
libzcash::AccountId accountId);
|
||||
|
||||
const CExtKey& GetTransparentKey() const {
|
||||
const transparent::AccountKey& GetTransparentKey() const {
|
||||
return transparentKey;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue