Merge pull request #5569 from str4d/feature/wallet_orchard-ua_integration
Integrate Orchard into Unified Address types
This commit is contained in:
commit
49065bee59
|
@ -8,7 +8,7 @@ replace-with = "vendored-sources"
|
|||
|
||||
[source."https://github.com/zcash/orchard.git"]
|
||||
git = "https://github.com/zcash/orchard.git"
|
||||
rev = "4dc1ae059a59ee911134cb3e731c7be627a71d4d"
|
||||
rev = "3b8d07f7b64b2329622089ac9698e4cce97e2f14"
|
||||
replace-with = "vendored-sources"
|
||||
|
||||
[source."https://github.com/nuttycom/hdwallet.git"]
|
||||
|
|
|
@ -1090,7 +1090,7 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
|||
[[package]]
|
||||
name = "orchard"
|
||||
version = "0.1.0-beta.1"
|
||||
source = "git+https://github.com/zcash/orchard.git?rev=4dc1ae059a59ee911134cb3e731c7be627a71d4d#4dc1ae059a59ee911134cb3e731c7be627a71d4d"
|
||||
source = "git+https://github.com/zcash/orchard.git?rev=3b8d07f7b64b2329622089ac9698e4cce97e2f14#3b8d07f7b64b2329622089ac9698e4cce97e2f14"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"arrayvec 0.7.2",
|
||||
|
|
|
@ -72,7 +72,7 @@ codegen-units = 1
|
|||
|
||||
[patch.crates-io]
|
||||
hdwallet = { git = "https://github.com/nuttycom/hdwallet", rev = "576683b9f2865f1118c309017ff36e01f84420c9" }
|
||||
orchard = { git = "https://github.com/zcash/orchard.git", rev = "4dc1ae059a59ee911134cb3e731c7be627a71d4d" }
|
||||
orchard = { git = "https://github.com/zcash/orchard.git", rev = "3b8d07f7b64b2329622089ac9698e4cce97e2f14" }
|
||||
zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "3d935a94e75786a67c3ea4992d7c372af203086f" }
|
||||
zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "3d935a94e75786a67c3ea4992d7c372af203086f" }
|
||||
zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "3d935a94e75786a67c3ea4992d7c372af203086f" }
|
||||
|
|
|
@ -73,6 +73,12 @@ namespace libzcash {
|
|||
public:
|
||||
ReceiverToString() {}
|
||||
|
||||
std::string operator()(const OrchardRawAddress &zaddr) const {
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << zaddr;
|
||||
return tfm::format("Orchard(%s)", HexStr(ss.begin(), ss.end()));
|
||||
}
|
||||
|
||||
std::string operator()(const SaplingPaymentAddress &zaddr) const {
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << zaddr;
|
||||
|
@ -126,8 +132,11 @@ TEST(Keys, EncodeAndDecodeUnifiedAddresses)
|
|||
// These were added to the UA in preference order by the Python test vectors.
|
||||
if (!test[3].isNull()) {
|
||||
auto data = ParseHex(test[3].get_str());
|
||||
libzcash::UnknownReceiver r(0x03, data);
|
||||
ua.AddReceiver(r);
|
||||
CDataStream ss(
|
||||
data,
|
||||
SER_NETWORK,
|
||||
PROTOCOL_VERSION);
|
||||
ua.AddReceiver(libzcash::OrchardRawAddress::Read(ss));
|
||||
}
|
||||
if (!test[2].isNull()) {
|
||||
auto data = ParseHex(test[2].get_str());
|
||||
|
@ -186,6 +195,16 @@ TEST(Keys, DeriveUnifiedFullViewingKeys)
|
|||
if (test.size() == 1) continue; // comment
|
||||
|
||||
try {
|
||||
// [
|
||||
// t_key_bytes,
|
||||
// sapling_fvk_bytes,
|
||||
// orchard_fvk_bytes,
|
||||
// unknown_fvk_typecode,
|
||||
// unknown_fvk_bytes,
|
||||
// unified_fvk,
|
||||
// root_seed,
|
||||
// account,
|
||||
// ],
|
||||
auto seed_hex = test[6].get_str();
|
||||
auto seed_data = ParseHex(seed_hex);
|
||||
RawHDSeed raw_seed(seed_data.begin(), seed_data.end());
|
||||
|
@ -235,6 +254,24 @@ TEST(Keys, DeriveUnifiedFullViewingKeys)
|
|||
auto key = libzcash::SaplingDiversifiableFullViewingKey::Read(ss);
|
||||
EXPECT_EQ(key, saplingKey);
|
||||
}
|
||||
if (!test[2].isNull()) {
|
||||
auto expectedHex = test[2].get_str();
|
||||
|
||||
// Ensure that the serialized Orchard fvk matches the test data.
|
||||
auto orchardKey = ufvk.GetOrchardKey().value();
|
||||
CDataStream ssEncode(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ssEncode << orchardKey;
|
||||
EXPECT_EQ(ssEncode.size(), 96);
|
||||
auto skeyHex = HexStr(ssEncode.begin(), ssEncode.end());
|
||||
EXPECT_EQ(expectedHex, skeyHex);
|
||||
|
||||
// Ensure that parsing the test data derives the correct dfvk
|
||||
auto data = ParseHex(expectedHex);
|
||||
ASSERT_EQ(data.size(), 96);
|
||||
CDataStream ss(data, SER_NETWORK, PROTOCOL_VERSION);
|
||||
auto key = libzcash::OrchardFullViewingKey::Read(ss);
|
||||
EXPECT_EQ(key, orchardKey);
|
||||
}
|
||||
// Enable the following after Orchard keys are supported.
|
||||
//{
|
||||
// auto fvk_data = ParseHex(test[5].get_str());
|
||||
|
@ -284,14 +321,13 @@ TEST(Keys, EncodeAndDecodeUnifiedFullViewingKeys)
|
|||
auto key = libzcash::SaplingDiversifiableFullViewingKey::Read(ss);
|
||||
ASSERT_TRUE(builder.AddSaplingKey(key));
|
||||
}
|
||||
|
||||
// Orchard keys and unknown items are not yet supported; instead,
|
||||
// we just test that we're able to parse the unified key string
|
||||
// and that the constituent items match the elements; if no Sapling
|
||||
// key is present then UFVK construction would fail because it might
|
||||
// presume the UFVK to be transparent-only.
|
||||
if (test[1].isNull())
|
||||
continue;
|
||||
if (!test[2].isNull()) {
|
||||
auto data = ParseHex(test[2].get_str());
|
||||
ASSERT_EQ(data.size(), 96);
|
||||
CDataStream ss(data, SER_NETWORK, PROTOCOL_VERSION);
|
||||
auto key = libzcash::OrchardFullViewingKey::Read(ss);
|
||||
ASSERT_TRUE(builder.AddOrchardKey(key));
|
||||
}
|
||||
|
||||
auto built = builder.build();
|
||||
ASSERT_TRUE(built.has_value());
|
||||
|
@ -304,5 +340,6 @@ TEST(Keys, EncodeAndDecodeUnifiedFullViewingKeys)
|
|||
|
||||
EXPECT_EQ(decoded.value().GetTransparentKey(), built.value().GetTransparentKey());
|
||||
EXPECT_EQ(decoded.value().GetSaplingKey(), built.value().GetSaplingKey());
|
||||
EXPECT_EQ(decoded.value().GetOrchardKey(), built.value().GetOrchardKey());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -557,17 +557,22 @@ TEST(KeystoreTests, StoreAndRetrieveUFVK) {
|
|||
EXPECT_EQ(keyStore.GetUnifiedFullViewingKey(ufvkid).value(), zufvk);
|
||||
|
||||
auto addrPair = std::get<std::pair<UnifiedAddress, diversifier_index_t>>(zufvk.FindAddress(diversifier_index_t(0), {ReceiverType::Sapling}));
|
||||
|
||||
|
||||
EXPECT_TRUE(addrPair.first.GetSaplingReceiver().has_value());
|
||||
auto saplingReceiver = addrPair.first.GetSaplingReceiver().value();
|
||||
auto ufvkmeta = keyStore.GetUFVKMetadataForReceiver(saplingReceiver);
|
||||
EXPECT_FALSE(ufvkmeta.has_value());
|
||||
|
||||
// We detect this even though we haven't added the Sapling address, because
|
||||
// we trial-decrypt diversifiers (which also means we learn the index).
|
||||
auto ufvkmetaUnadded = keyStore.GetUFVKMetadataForReceiver(saplingReceiver);
|
||||
EXPECT_TRUE(ufvkmetaUnadded.has_value());
|
||||
EXPECT_EQ(ufvkmetaUnadded.value().first, ufvkid);
|
||||
EXPECT_EQ(ufvkmetaUnadded.value().second.value(), addrPair.second);
|
||||
|
||||
// Adding the Sapling addr -> ivk map entry causes us to find the same UFVK,
|
||||
// but as we're no longer trial-decrypting we don't learn the index.
|
||||
auto saplingIvk = zufvk.GetSaplingKey().value().ToIncomingViewingKey();
|
||||
keyStore.AddSaplingPaymentAddress(saplingIvk, saplingReceiver);
|
||||
|
||||
ufvkmeta = keyStore.GetUFVKMetadataForReceiver(saplingReceiver);
|
||||
auto ufvkmeta = keyStore.GetUFVKMetadataForReceiver(saplingReceiver);
|
||||
EXPECT_TRUE(ufvkmeta.has_value());
|
||||
EXPECT_EQ(ufvkmeta.value().first, ufvkid);
|
||||
EXPECT_FALSE(ufvkmeta.value().second.has_value());
|
||||
|
|
|
@ -56,6 +56,7 @@ class DataLenForReceiver {
|
|||
public:
|
||||
DataLenForReceiver() {}
|
||||
|
||||
size_t operator()(const libzcash::OrchardRawAddress &zaddr) const { return 43; }
|
||||
size_t operator()(const libzcash::SaplingPaymentAddress &zaddr) const { return 43; }
|
||||
size_t operator()(const CScriptID &p2sh) const { return 20; }
|
||||
size_t operator()(const CKeyID &p2pkh) const { return 20; }
|
||||
|
@ -76,6 +77,13 @@ class CopyDataForReceiver {
|
|||
public:
|
||||
CopyDataForReceiver(unsigned char* data, size_t length) : data(data), length(length) {}
|
||||
|
||||
void operator()(const libzcash::OrchardRawAddress &zaddr) const {
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << zaddr;
|
||||
assert(length == ss.size());
|
||||
memcpy(data, ss.data(), ss.size());
|
||||
}
|
||||
|
||||
void operator()(const libzcash::SaplingPaymentAddress &zaddr) const {
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << zaddr;
|
||||
|
@ -433,6 +441,12 @@ std::optional<T1> DecodeAny(
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
static bool AddOrchardReceiver(void* ua, OrchardRawAddressPtr* ptr)
|
||||
{
|
||||
return reinterpret_cast<libzcash::UnifiedAddress*>(ua)->AddReceiver(
|
||||
libzcash::OrchardRawAddress::KeyIoOnlyFromReceiver(ptr));
|
||||
}
|
||||
|
||||
/**
|
||||
* `raw` MUST be 43 bytes.
|
||||
*/
|
||||
|
@ -492,6 +506,7 @@ std::optional<libzcash::PaymentAddress> KeyIO::DecodePaymentAddress(const std::s
|
|||
str.c_str(),
|
||||
keyConstants.NetworkIDString().c_str(),
|
||||
&ua,
|
||||
AddOrchardReceiver,
|
||||
AddSaplingReceiver,
|
||||
AddP2SHReceiver,
|
||||
AddP2PKHReceiver,
|
||||
|
|
|
@ -252,6 +252,10 @@ bool CBasicKeyStore::GetSproutViewingKey(
|
|||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// Sapling Keys
|
||||
//
|
||||
|
||||
bool CBasicKeyStore::GetSaplingFullViewingKey(
|
||||
const libzcash::SaplingIncomingViewingKey &ivk,
|
||||
libzcash::SaplingExtendedFullViewingKey &extfvkOut) const
|
||||
|
@ -308,14 +312,26 @@ bool CBasicKeyStore::AddUnifiedFullViewingKey(
|
|||
{
|
||||
LOCK(cs_KeyStore);
|
||||
|
||||
auto ufvkId = ufvk.GetKeyID();
|
||||
|
||||
// Add the Orchard component of the UFVK to the wallet.
|
||||
auto orchardKey = ufvk.GetOrchardKey();
|
||||
if (orchardKey.has_value()) {
|
||||
auto ivk = orchardKey.value().ToIncomingViewingKey();
|
||||
mapOrchardKeyUnified.insert(std::make_pair(ivk, ufvkId));
|
||||
|
||||
auto ivkInternal = orchardKey.value().ToInternalIncomingViewingKey();
|
||||
mapOrchardKeyUnified.insert(std::make_pair(ivkInternal, ufvkId));
|
||||
}
|
||||
|
||||
// Add the Sapling component of the UFVK to the wallet.
|
||||
auto saplingKey = ufvk.GetSaplingKey();
|
||||
if (saplingKey.has_value()) {
|
||||
auto ivk = saplingKey.value().ToIncomingViewingKey();
|
||||
mapSaplingKeyUnified.insert(std::make_pair(ivk, ufvk.GetKeyID()));
|
||||
mapSaplingKeyUnified.insert(std::make_pair(ivk, ufvkId));
|
||||
|
||||
auto changeIvk = saplingKey.value().GetChangeIVK();
|
||||
mapSaplingKeyUnified.insert(std::make_pair(changeIvk, ufvk.GetKeyID()));
|
||||
mapSaplingKeyUnified.insert(std::make_pair(changeIvk, ufvkId));
|
||||
}
|
||||
|
||||
// We can't reasonably add the transparent component here, because
|
||||
|
@ -325,7 +341,7 @@ bool CBasicKeyStore::AddUnifiedFullViewingKey(
|
|||
// transparent part of the address must be added to the keystore.
|
||||
|
||||
// Add the UFVK by key identifier.
|
||||
mapUnifiedFullViewingKeys.insert({ufvk.GetKeyID(), ufvk});
|
||||
mapUnifiedFullViewingKeys.insert({ufvkId, ufvk});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -386,6 +402,15 @@ std::optional<libzcash::UFVKId> CBasicKeyStore::GetUFVKIdForViewingKey(const lib
|
|||
}
|
||||
},
|
||||
[&](const libzcash::UnifiedFullViewingKey& ufvk) {
|
||||
const auto orchardFvk = ufvk.GetOrchardKey();
|
||||
if (orchardFvk.has_value()) {
|
||||
const auto orchardIvk = orchardFvk.value().ToIncomingViewingKey();
|
||||
const auto ufvkId = mapOrchardKeyUnified.find(orchardIvk);
|
||||
if (ufvkId != mapOrchardKeyUnified.end()) {
|
||||
result = ufvkId->second;
|
||||
return;
|
||||
}
|
||||
}
|
||||
const auto saplingDfvk = ufvk.GetSaplingKey();
|
||||
if (saplingDfvk.has_value()) {
|
||||
const auto saplingIvk = saplingDfvk.value().ToIncomingViewingKey();
|
||||
|
@ -399,19 +424,54 @@ std::optional<libzcash::UFVKId> CBasicKeyStore::GetUFVKIdForViewingKey(const lib
|
|||
return result;
|
||||
}
|
||||
|
||||
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
|
||||
FindUFVKId::operator()(const libzcash::OrchardRawAddress& orchardAddr) const {
|
||||
for (const auto& [k, v] : keystore.mapUnifiedFullViewingKeys) {
|
||||
auto fvk = v.GetOrchardKey();
|
||||
if (fvk.has_value()) {
|
||||
auto d_idx = fvk.value().ToIncomingViewingKey().DecryptDiversifier(orchardAddr);
|
||||
if (d_idx.has_value()) {
|
||||
return std::make_pair(k, d_idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
|
||||
FindUFVKId::operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const {
|
||||
const auto saplingIvk = keystore.mapSaplingIncomingViewingKeys.find(saplingAddr);
|
||||
if (saplingIvk != keystore.mapSaplingIncomingViewingKeys.end()) {
|
||||
// We have either generated this as a receiver via `z_getaddressforaccount` or a
|
||||
// legacy Sapling address via `z_getnewaddress`, or we have previously detected
|
||||
// this via trial-decryption of a note.
|
||||
const auto ufvkId = keystore.mapSaplingKeyUnified.find(saplingIvk->second);
|
||||
if (ufvkId != keystore.mapSaplingKeyUnified.end()) {
|
||||
return std::make_pair(ufvkId->second, std::nullopt);
|
||||
} else {
|
||||
// If we have the addr -> ivk map entry but not the ivk -> UFVK map entry,
|
||||
// then this is definitely a legacy Sapling address.
|
||||
return std::nullopt;
|
||||
}
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// We haven't generated this receiver via `z_getaddressforaccount` (or this is a
|
||||
// recovery from a backed-up mnemonic which doesn't store receiver types selected by
|
||||
// users). Trial-decrypt the diversifier of the Sapling address with every UFVK in the
|
||||
// wallet, to check directly if it belongs to any of them.
|
||||
for (const auto& [k, v] : keystore.mapUnifiedFullViewingKeys) {
|
||||
auto dfvk = v.GetSaplingKey();
|
||||
if (dfvk.has_value()) {
|
||||
auto d_idx = dfvk.value().DecryptDiversifier(saplingAddr.d);
|
||||
auto derived_addr = dfvk.value().Address(d_idx);
|
||||
if (derived_addr.has_value() && derived_addr.value() == saplingAddr) {
|
||||
return std::make_pair(k, d_idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We definitely don't know of any UFVK linked to this Sapling address.
|
||||
return std::nullopt;
|
||||
}
|
||||
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
|
||||
FindUFVKId::operator()(const CScriptID& scriptId) const {
|
||||
|
|
|
@ -185,6 +185,7 @@ protected:
|
|||
std::map<CKeyID, std::pair<libzcash::UFVKId, libzcash::diversifier_index_t>> mapP2PKHUnified;
|
||||
std::map<CScriptID, std::pair<libzcash::UFVKId, libzcash::diversifier_index_t>> mapP2SHUnified;
|
||||
std::map<libzcash::SaplingIncomingViewingKey, libzcash::UFVKId> mapSaplingKeyUnified;
|
||||
std::map<libzcash::OrchardIncomingViewingKey, libzcash::UFVKId> mapOrchardKeyUnified;
|
||||
std::map<libzcash::UFVKId, libzcash::ZcashdUnifiedFullViewingKey> mapUnifiedFullViewingKeys;
|
||||
|
||||
friend class FindUFVKId;
|
||||
|
@ -398,6 +399,8 @@ private:
|
|||
public:
|
||||
FindUFVKId(const CBasicKeyStore& keystore): keystore(keystore) {}
|
||||
|
||||
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
|
||||
operator()(const libzcash::OrchardRawAddress& orchardAddr) const;
|
||||
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
|
||||
operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const;
|
||||
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
|
||||
|
|
|
@ -5,10 +5,13 @@
|
|||
#ifndef ZCASH_RUST_INCLUDE_RUST_ADDRESS_H
|
||||
#define ZCASH_RUST_INCLUDE_RUST_ADDRESS_H
|
||||
|
||||
#include "rust/orchard/keys.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef bool (*orchard_receiver_t)(void* ua, OrchardRawAddressPtr* addr);
|
||||
typedef bool (*raw_to_receiver_t)(void* ua, const unsigned char* raw);
|
||||
typedef bool (*unknown_receiver_t)(
|
||||
void* ua,
|
||||
|
@ -24,6 +27,7 @@ bool zcash_address_parse_unified(
|
|||
const char* str,
|
||||
const char* network,
|
||||
void* ua,
|
||||
orchard_receiver_t orchard_cb,
|
||||
raw_to_receiver_t sapling_cb,
|
||||
raw_to_receiver_t p2sh_cb,
|
||||
raw_to_receiver_t p2pkh_cb,
|
||||
|
|
|
@ -32,6 +32,34 @@ OrchardRawAddressPtr* orchard_address_clone(
|
|||
*/
|
||||
void orchard_address_free(OrchardRawAddressPtr* ptr);
|
||||
|
||||
/**
|
||||
* Parses Orchard raw address bytes from the given stream.
|
||||
*
|
||||
* - If the key does not parse correctly, the returned pointer will be null.
|
||||
*/
|
||||
OrchardRawAddressPtr* orchard_raw_address_parse(
|
||||
void* stream,
|
||||
read_callback_t read_cb);
|
||||
|
||||
|
||||
/**
|
||||
* Serializes Orchard raw address bytes to the given stream.
|
||||
*
|
||||
* This will return `false` and leave the stream unmodified if
|
||||
* `raw_address == nullptr`;
|
||||
*/
|
||||
bool orchard_raw_address_serialize(
|
||||
const OrchardRawAddressPtr* raw_address,
|
||||
void* stream,
|
||||
write_callback_t write_cb);
|
||||
|
||||
/**
|
||||
* Implements the "equal" operation for comparing two Orchard addresses.
|
||||
*/
|
||||
bool orchard_address_eq(
|
||||
const OrchardRawAddressPtr* k0,
|
||||
const OrchardRawAddressPtr* k1);
|
||||
|
||||
/**
|
||||
* Implements the "less than" operation `k0 < k1` for comparing two Orchard
|
||||
* addresses. This is a comparison of the raw bytes, only useful for cases
|
||||
|
@ -69,6 +97,18 @@ OrchardRawAddressPtr* orchard_incoming_viewing_key_to_address(
|
|||
const OrchardIncomingViewingKeyPtr* incoming_viewing_key,
|
||||
const unsigned char* j);
|
||||
|
||||
/**
|
||||
* Decrypts the diversifier component of an Orchard raw address with the
|
||||
* specified IVK, and verifies that the address was derived from that IVK.
|
||||
*
|
||||
* Returns `false` and leaves the `j_ret` parameter unmodified if the address
|
||||
* was not derived from the specified IVK.
|
||||
*/
|
||||
bool orchard_incoming_viewing_key_decrypt_diversifier(
|
||||
const OrchardIncomingViewingKeyPtr* incoming_viewing_key,
|
||||
const OrchardRawAddressPtr* addr,
|
||||
uint8_t *j_ret);
|
||||
|
||||
/**
|
||||
* Parses an Orchard incoming viewing key from the given stream.
|
||||
*
|
||||
|
@ -151,6 +191,12 @@ bool orchard_full_viewing_key_serialize(
|
|||
OrchardIncomingViewingKeyPtr* orchard_full_viewing_key_to_incoming_viewing_key(
|
||||
const OrchardFullViewingKeyPtr* key);
|
||||
|
||||
/**
|
||||
* Returns the internal incoming viewing key for the specified full viewing key.
|
||||
*/
|
||||
OrchardIncomingViewingKeyPtr* orchard_full_viewing_key_to_internal_incoming_viewing_key(
|
||||
const OrchardFullViewingKeyPtr* key);
|
||||
|
||||
/**
|
||||
* Implements equality testing between full viewing keys.
|
||||
*/
|
||||
|
@ -189,26 +235,6 @@ OrchardSpendingKeyPtr* orchard_spending_key_clone(
|
|||
*/
|
||||
void orchard_spending_key_free(OrchardSpendingKeyPtr* ptr);
|
||||
|
||||
/**
|
||||
* Parses an Orchard spending key from the given stream.
|
||||
*
|
||||
* - If the key does not parse correctly, the returned pointer will be null.
|
||||
*/
|
||||
OrchardSpendingKeyPtr* orchard_spending_key_parse(
|
||||
void* stream,
|
||||
read_callback_t read_cb);
|
||||
|
||||
/**
|
||||
* Serializes an Orchard spending key to the given stream.
|
||||
*
|
||||
* This will return `false` and leave the stream unmodified if
|
||||
* `spending_key == nullptr`.
|
||||
*/
|
||||
bool orchard_spending_key_serialize(
|
||||
const OrchardSpendingKeyPtr* spending_key,
|
||||
void* stream,
|
||||
write_callback_t write_cb);
|
||||
|
||||
/**
|
||||
* Returns the full viewing key for the specified spending key.
|
||||
*
|
||||
|
|
|
@ -84,6 +84,22 @@ bool unified_full_viewing_key_read_sapling(
|
|||
const UnifiedFullViewingKeyPtr* full_viewing_key,
|
||||
unsigned char* skeyout);
|
||||
|
||||
/**
|
||||
* Reads the Orchard component of a unified full viewing key.
|
||||
*
|
||||
* `skeyout` must be of length 96.
|
||||
*
|
||||
* Returns `true` if the UFVK contained an Orchard component, `false` otherwise.
|
||||
* The bytes of the Orchard Raw Full Viewing Key, in the encoding given in
|
||||
* section 5.6.4.4 of the Zcash Protocol Specification, will be copied to
|
||||
* `skeyout` if `true` is returned.
|
||||
*
|
||||
* If `false` is returned then `skeyout` will be unchanged.
|
||||
*/
|
||||
bool unified_full_viewing_key_read_orchard(
|
||||
const UnifiedFullViewingKeyPtr* full_viewing_key,
|
||||
unsigned char* skeyout);
|
||||
|
||||
/**
|
||||
* Constructs a unified full viewing key from the binary encodings
|
||||
* of its constituent parts.
|
||||
|
@ -101,7 +117,8 @@ bool unified_full_viewing_key_read_sapling(
|
|||
*/
|
||||
UnifiedFullViewingKeyPtr* unified_full_viewing_key_from_components(
|
||||
const unsigned char* t_key,
|
||||
const unsigned char* sapling_key);
|
||||
const unsigned char* sapling_key,
|
||||
const unsigned char* orchard_key);
|
||||
|
||||
/**
|
||||
* Derive the internal and external OVKs for the binary encoding
|
||||
|
|
|
@ -12,6 +12,8 @@ use zcash_address::{
|
|||
use zcash_primitives::sapling;
|
||||
|
||||
pub type UnifiedAddressObj = NonNull<c_void>;
|
||||
pub type AddOrchardReceiverCb =
|
||||
unsafe extern "C" fn(ua: Option<UnifiedAddressObj>, orchard: *const orchard::Address) -> bool;
|
||||
pub type AddReceiverCb =
|
||||
unsafe extern "C" fn(ua: Option<UnifiedAddressObj>, raw: *const u8) -> bool;
|
||||
pub type UnknownReceiverCb = unsafe extern "C" fn(
|
||||
|
@ -53,10 +55,12 @@ impl FromAddress for UnifiedAddressHelper {
|
|||
}
|
||||
|
||||
impl UnifiedAddressHelper {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn into_cpp(
|
||||
self,
|
||||
network: Network,
|
||||
ua_obj: Option<UnifiedAddressObj>,
|
||||
orchard_cb: Option<AddOrchardReceiverCb>,
|
||||
sapling_cb: Option<AddReceiverCb>,
|
||||
p2sh_cb: Option<AddReceiverCb>,
|
||||
p2pkh_cb: Option<AddReceiverCb>,
|
||||
|
@ -79,16 +83,13 @@ impl UnifiedAddressHelper {
|
|||
// ZIP 316: Consumers MUST reject Unified Addresses/Viewing Keys in
|
||||
// which any constituent Item does not meet the validation
|
||||
// requirements of its encoding.
|
||||
if orchard::Address::from_raw_address_bytes(&data)
|
||||
.is_none()
|
||||
.into()
|
||||
{
|
||||
let addr = orchard::Address::from_raw_address_bytes(&data);
|
||||
if addr.is_none().into() {
|
||||
tracing::error!("Unified Address contains invalid Orchard receiver");
|
||||
false
|
||||
} else {
|
||||
unsafe {
|
||||
// TODO: Replace with Orchard support.
|
||||
(unknown_cb.unwrap())(ua_obj, 0x03, data.as_ptr(), data.len())
|
||||
(orchard_cb.unwrap())(ua_obj, Box::into_raw(Box::new(addr.unwrap())))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -122,6 +123,7 @@ pub extern "C" fn zcash_address_parse_unified(
|
|||
encoded: *const c_char,
|
||||
network: *const c_char,
|
||||
ua_obj: Option<UnifiedAddressObj>,
|
||||
orchard_cb: Option<AddOrchardReceiverCb>,
|
||||
sapling_cb: Option<AddReceiverCb>,
|
||||
p2sh_cb: Option<AddReceiverCb>,
|
||||
p2pkh_cb: Option<AddReceiverCb>,
|
||||
|
@ -149,7 +151,9 @@ pub extern "C" fn zcash_address_parse_unified(
|
|||
}
|
||||
};
|
||||
|
||||
ua.into_cpp(network, ua_obj, sapling_cb, p2sh_cb, p2pkh_cb, unknown_cb)
|
||||
ua.into_cpp(
|
||||
network, ua_obj, orchard_cb, sapling_cb, p2sh_cb, p2pkh_cb, unknown_cb,
|
||||
)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
@ -171,14 +175,9 @@ pub extern "C" fn zcash_address_serialize_unified(
|
|||
Ok(
|
||||
match unsafe { (typecode_cb.unwrap())(ua_obj, i) }.try_into()? {
|
||||
unified::Typecode::Orchard => {
|
||||
// TODO: Replace with Orchard support.
|
||||
let data_len = unsafe { (receiver_len_cb.unwrap())(ua_obj, i) };
|
||||
let mut data = vec![0; data_len];
|
||||
unsafe { (receiver_cb.unwrap())(ua_obj, i, data.as_mut_ptr(), data_len) };
|
||||
unified::Receiver::Unknown {
|
||||
typecode: 0x03,
|
||||
data,
|
||||
}
|
||||
let mut data = [0; 43];
|
||||
unsafe { (receiver_cb.unwrap())(ua_obj, i, data.as_mut_ptr(), data.len()) };
|
||||
unified::Receiver::Orchard(data)
|
||||
}
|
||||
unified::Typecode::Sapling => {
|
||||
let mut data = [0; 43];
|
||||
|
|
|
@ -28,6 +28,56 @@ pub extern "C" fn orchard_address_free(addr: *mut Address) {
|
|||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn orchard_raw_address_parse(
|
||||
stream: Option<StreamObj>,
|
||||
read_cb: Option<ReadCb>,
|
||||
) -> *mut Address {
|
||||
let mut reader = CppStreamReader::from_raw_parts(stream, read_cb.unwrap());
|
||||
|
||||
let mut buf = [0u8; 43];
|
||||
match reader.read_exact(&mut buf) {
|
||||
Err(e) => {
|
||||
error!("Stream failure reading bytes of Orchard raw address: {}", e);
|
||||
std::ptr::null_mut()
|
||||
}
|
||||
Ok(()) => {
|
||||
let read = Address::from_raw_address_bytes(&buf);
|
||||
if read.is_some().into() {
|
||||
Box::into_raw(Box::new(read.unwrap()))
|
||||
} else {
|
||||
error!("Failed to parse Orchard raw address.");
|
||||
std::ptr::null_mut()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn orchard_raw_address_serialize(
|
||||
key: *const Address,
|
||||
stream: Option<StreamObj>,
|
||||
write_cb: Option<WriteCb>,
|
||||
) -> bool {
|
||||
let key = unsafe { key.as_ref() }.expect("Orchard raw address pointer may not be null.");
|
||||
|
||||
let mut writer = CppStreamWriter::from_raw_parts(stream, write_cb.unwrap());
|
||||
match writer.write_all(&key.to_raw_address_bytes()) {
|
||||
Ok(()) => true,
|
||||
Err(e) => {
|
||||
error!("Stream failure writing Orchard raw address: {}", e);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn orchard_address_eq(a0: *const Address, a1: *const Address) -> bool {
|
||||
let a0 = unsafe { a0.as_ref() };
|
||||
let a1 = unsafe { a1.as_ref() };
|
||||
a0 == a1
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn orchard_address_lt(a0: *const Address, a1: *const Address) -> bool {
|
||||
let a0 = unsafe { a0.as_ref() };
|
||||
|
@ -95,6 +145,26 @@ pub extern "C" fn orchard_incoming_viewing_key_to_address(
|
|||
Box::into_raw(Box::new(key.address_at(diversifier_index)))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn orchard_incoming_viewing_key_decrypt_diversifier(
|
||||
key: *const IncomingViewingKey,
|
||||
addr: *const Address,
|
||||
j_ret: *mut [u8; 11],
|
||||
) -> bool {
|
||||
let key =
|
||||
unsafe { key.as_ref() }.expect("Orchard incoming viewing key pointer may not be null.");
|
||||
let addr = unsafe { addr.as_ref() }.expect("Orchard raw address pointer may not be null.");
|
||||
let j_ret = unsafe { j_ret.as_mut() }.expect("j_ret may not be null.");
|
||||
|
||||
match key.diversifier_index(addr) {
|
||||
Some(j) => {
|
||||
j_ret.copy_from_slice(j.to_bytes());
|
||||
true
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn orchard_incoming_viewing_key_serialize(
|
||||
key: *const IncomingViewingKey,
|
||||
|
@ -200,6 +270,18 @@ pub extern "C" fn orchard_full_viewing_key_to_incoming_viewing_key(
|
|||
.unwrap_or(std::ptr::null_mut())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn orchard_full_viewing_key_to_internal_incoming_viewing_key(
|
||||
fvk: *const FullViewingKey,
|
||||
) -> *mut IncomingViewingKey {
|
||||
unsafe { fvk.as_ref() }
|
||||
.map(|fvk| {
|
||||
let internal_fvk = fvk.derive_internal();
|
||||
Box::into_raw(Box::new(IncomingViewingKey::from(&internal_fvk)))
|
||||
})
|
||||
.unwrap_or(std::ptr::null_mut())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn orchard_full_viewing_key_eq(
|
||||
k0: *const FullViewingKey,
|
||||
|
|
|
@ -139,13 +139,33 @@ pub extern "C" fn unified_full_viewing_key_read_sapling(
|
|||
false
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn unified_full_viewing_key_read_orchard(
|
||||
key: *const Ufvk,
|
||||
out: *mut [u8; 96],
|
||||
) -> bool {
|
||||
let key = unsafe { key.as_ref() }.expect("Unified full viewing key pointer may not be null.");
|
||||
let out = unsafe { &mut *out };
|
||||
|
||||
for r in &key.items() {
|
||||
if let Fvk::Orchard(data) = r {
|
||||
*out = *data;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn unified_full_viewing_key_from_components(
|
||||
t_key: *const [u8; 65],
|
||||
sapling_key: *const [u8; 128],
|
||||
orchard_key: *const [u8; 96],
|
||||
) -> *mut Ufvk {
|
||||
let t_key = unsafe { t_key.as_ref() };
|
||||
let sapling_key = unsafe { sapling_key.as_ref() };
|
||||
let orchard_key = unsafe { orchard_key.as_ref() };
|
||||
|
||||
let mut items = vec![];
|
||||
if let Some(t_bytes) = t_key {
|
||||
|
@ -154,6 +174,9 @@ pub extern "C" fn unified_full_viewing_key_from_components(
|
|||
if let Some(sapling_bytes) = sapling_key {
|
||||
items.push(Fvk::Sapling(*sapling_bytes));
|
||||
}
|
||||
if let Some(orchard_bytes) = orchard_key {
|
||||
items.push(Fvk::Orchard(*orchard_bytes));
|
||||
}
|
||||
|
||||
match Ufvk::try_from_items(items) {
|
||||
Ok(ufvk) => Box::into_raw(Box::new(ufvk)),
|
||||
|
|
|
@ -340,6 +340,9 @@ uint256 AsyncRPCOperation_sendmany::main_impl() {
|
|||
case ReceiverType::Sapling:
|
||||
allowedChangeTypes_.insert(libzcash::ChangeType::Sapling);
|
||||
break;
|
||||
case ReceiverType::Orchard:
|
||||
// TODO
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,19 +33,3 @@ TEST(OrchardZkeysTest, FVKSerializationRoundtrip) {
|
|||
|
||||
ASSERT_EQ(fvk, fvk0);
|
||||
}
|
||||
|
||||
TEST(OrchardZkeysTest, SKSerializationRoundtrip) {
|
||||
auto seed = MnemonicSeed::Random(1); //testnet coin type
|
||||
|
||||
auto sk = libzcash::OrchardSpendingKey::ForAccount(seed, 1, 0);
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << sk;
|
||||
std::string skStr = ss.str();
|
||||
|
||||
auto sk0 = libzcash::OrchardSpendingKey::Read(ss);
|
||||
CDataStream ss0(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss0 << sk0;
|
||||
std::string sk0Str = ss0.str();
|
||||
|
||||
ASSERT_EQ(skStr, sk0Str);
|
||||
}
|
||||
|
|
|
@ -2200,14 +2200,15 @@ TEST(WalletTests, GenerateUnifiedAddress) {
|
|||
EXPECT_EQ(uaResult, expected);
|
||||
|
||||
// Create an account, then generate an address for that account.
|
||||
auto skpair = wallet.GenerateNewUnifiedSpendingKey();
|
||||
uaResult = wallet.GenerateUnifiedAddress(skpair.second, {ReceiverType::P2PKH, ReceiverType::Sapling});
|
||||
auto ufvkpair = wallet.GenerateNewUnifiedSpendingKey();
|
||||
auto ufvk = ufvkpair.first;
|
||||
auto account = ufvkpair.second;
|
||||
uaResult = wallet.GenerateUnifiedAddress(account, {ReceiverType::P2PKH, ReceiverType::Sapling});
|
||||
auto ua = std::get_if<std::pair<libzcash::UnifiedAddress, libzcash::diversifier_index_t>>(&uaResult);
|
||||
EXPECT_NE(ua, nullptr);
|
||||
|
||||
auto uaSaplingReceiver = ua->first.GetSaplingReceiver();
|
||||
EXPECT_TRUE(uaSaplingReceiver.has_value());
|
||||
auto ufvk = skpair.first.ToFullViewingKey();
|
||||
EXPECT_EQ(uaSaplingReceiver.value(), ufvk.GetSaplingKey().value().Address(ua->second));
|
||||
|
||||
auto u4r = wallet.FindUnifiedAddressByReceiver(uaSaplingReceiver.value());
|
||||
|
|
|
@ -3057,8 +3057,8 @@ UniValue z_getnewaccount(const UniValue& params, bool fHelp)
|
|||
EnsureWalletIsUnlocked();
|
||||
|
||||
// Generate the new account.
|
||||
auto skNew = pwalletMain->GenerateNewUnifiedSpendingKey();
|
||||
const auto& account = skNew.second;
|
||||
auto ufvkNew = pwalletMain->GenerateNewUnifiedSpendingKey();
|
||||
const auto& account = ufvkNew.second;
|
||||
|
||||
UniValue result(UniValue::VOBJ);
|
||||
result.pushKV("account", (uint64_t)account);
|
||||
|
|
|
@ -477,7 +477,7 @@ libzcash::transparent::AccountKey CWallet::GetLegacyAccountKey() const {
|
|||
}
|
||||
|
||||
|
||||
std::pair<ZcashdUnifiedSpendingKey, libzcash::AccountId> CWallet::GenerateNewUnifiedSpendingKey() {
|
||||
std::pair<UnifiedFullViewingKey, libzcash::AccountId> CWallet::GenerateNewUnifiedSpendingKey() {
|
||||
AssertLockHeld(cs_wallet);
|
||||
|
||||
if (!mnemonicHDChain.has_value()) {
|
||||
|
@ -488,17 +488,17 @@ std::pair<ZcashdUnifiedSpendingKey, libzcash::AccountId> CWallet::GenerateNewUni
|
|||
CHDChain& hdChain = mnemonicHDChain.value();
|
||||
while (true) {
|
||||
auto accountId = hdChain.GetAccountCounter();
|
||||
auto usk = GenerateUnifiedSpendingKeyForAccount(accountId);
|
||||
auto generated = GenerateUnifiedSpendingKeyForAccount(accountId);
|
||||
hdChain.IncrementAccountCounter();
|
||||
|
||||
if (usk.has_value()) {
|
||||
if (generated.has_value()) {
|
||||
// Update the persisted chain information
|
||||
if (fFileBacked && !CWalletDB(strWalletFile).WriteMnemonicHDChain(hdChain)) {
|
||||
throw std::runtime_error(
|
||||
"CWallet::GenerateNewUnifiedSpendingKey(): Writing HD chain model failed");
|
||||
}
|
||||
|
||||
return std::make_pair(usk.value(), accountId);
|
||||
return std::make_pair(generated.value().ToFullViewingKey(), accountId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -554,7 +554,7 @@ std::optional<libzcash::ZcashdUnifiedSpendingKey>
|
|||
);
|
||||
|
||||
// Add the Sapling spending key to the wallet
|
||||
auto saplingEsk = usk.value().GetSaplingExtendedSpendingKey();
|
||||
auto saplingEsk = usk.value().GetSaplingKey();
|
||||
if (addSaplingKey(saplingEsk) == KeyNotAdded) {
|
||||
// If adding the Sapling key to the wallet failed, abort the process.
|
||||
throw std::runtime_error("CWalletDB::GenerateUnifiedSpendingKeyForAccount(): Unable to add Sapling spending key to the wallet.");
|
||||
|
@ -576,7 +576,8 @@ std::optional<libzcash::ZcashdUnifiedSpendingKey>
|
|||
throw std::runtime_error("CWallet::GenerateUnifiedSpendingKeyForAccount(): Failed to add Sapling change address to the wallet.");
|
||||
};
|
||||
|
||||
// TODO ORCHARD: Add Orchard component to the wallet
|
||||
// Add Orchard spending key to the wallet
|
||||
orchardWallet.AddSpendingKey(usk.value().GetOrchardKey());
|
||||
|
||||
auto zufvk = ZcashdUnifiedFullViewingKey::FromUnifiedFullViewingKey(Params(), ufvk);
|
||||
if (!CCryptoKeyStore::AddUnifiedFullViewingKey(zufvk)) {
|
||||
|
@ -725,8 +726,9 @@ WalletUAGenerationResult CWallet::GenerateUnifiedAddress(
|
|||
assert(mapUfvkAddressMetadata[ufvkid].SetReceivers(address.second, receiverTypes));
|
||||
if (hasTransparent) {
|
||||
// We must construct and add the transparent spending key associated
|
||||
// with the external transparent child address to the transparent
|
||||
// keystore.
|
||||
// with the external and internal transparent child addresses to the
|
||||
// transparent keystore. This call to `value` will succeed because
|
||||
// this key must have been previously generated.
|
||||
auto usk = GenerateUnifiedSpendingKeyForAccount(accountId).value();
|
||||
auto accountKey = usk.GetTransparentKey();
|
||||
// this .value is known to be safe from the earlier check
|
||||
|
@ -6425,6 +6427,9 @@ std::optional<libzcash::ViewingKey> GetViewingKeyForPaymentAddress::operator()(
|
|||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// GetViewingKeyForPaymentAddress visitor
|
||||
|
||||
std::optional<libzcash::ViewingKey> GetViewingKeyForPaymentAddress::operator()(
|
||||
const libzcash::SproutPaymentAddress &zaddr) const
|
||||
{
|
||||
|
@ -6551,6 +6556,20 @@ KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SaplingExtendedS
|
|||
|
||||
// UFVKForReceiver :: (CWallet&, Receiver) -> std::optional<ZcashdUnifiedFullViewingKey>
|
||||
|
||||
std::optional<libzcash::ZcashdUnifiedFullViewingKey> UFVKForReceiver::operator()(const libzcash::OrchardRawAddress& orchardAddr) const {
|
||||
auto ufvkPair = wallet.GetUFVKMetadataForReceiver(orchardAddr);
|
||||
if (ufvkPair.has_value()) {
|
||||
auto ufvkid = ufvkPair.value().first;
|
||||
auto ufvk = wallet.GetUnifiedFullViewingKey(ufvkid);
|
||||
// If we have UFVK metadata, `GetUnifiedFullViewingKey` should always
|
||||
// return a non-nullopt value, and since we obtained that metadata by
|
||||
// lookup from an Orchard address, it should have a Orchard key component.
|
||||
assert(ufvk.has_value() && ufvk.value().GetOrchardKey().has_value());
|
||||
return ufvk.value();
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
std::optional<libzcash::ZcashdUnifiedFullViewingKey> UFVKForReceiver::operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const {
|
||||
auto ufvkPair = wallet.GetUFVKMetadataForReceiver(saplingAddr);
|
||||
if (ufvkPair.has_value()) {
|
||||
|
@ -6590,6 +6609,35 @@ std::optional<libzcash::ZcashdUnifiedFullViewingKey> UFVKForReceiver::operator()
|
|||
|
||||
// UnifiedAddressForReceiver :: (CWallet&, Receiver) -> std::optional<UnifiedAddress>
|
||||
|
||||
std::optional<libzcash::UnifiedAddress> UnifiedAddressForReceiver::operator()(
|
||||
const libzcash::OrchardRawAddress& orchardAddr) const {
|
||||
auto ufvkPair = wallet.GetUFVKMetadataForReceiver(orchardAddr);
|
||||
if (ufvkPair.has_value()) {
|
||||
auto ufvkid = ufvkPair.value().first;
|
||||
auto ufvk = wallet.GetUnifiedFullViewingKey(ufvkid);
|
||||
assert(ufvk.has_value());
|
||||
|
||||
// If the wallet is missing metadata at this UFVK id, it is probably
|
||||
// corrupt and the node should shut down.
|
||||
const auto& metadata = wallet.mapUfvkAddressMetadata.at(ufvkid);
|
||||
auto orchardKey = ufvk.value().GetOrchardKey();
|
||||
if (orchardKey.has_value()) {
|
||||
auto j = orchardKey.value().ToIncomingViewingKey().DecryptDiversifier(orchardAddr);
|
||||
if (j.has_value()) {
|
||||
auto receivers = metadata.GetReceivers(j.value());
|
||||
if (receivers.has_value()) {
|
||||
auto addr = ufvk.value().Address(j.value(), receivers.value());
|
||||
auto addrPtr = std::get_if<std::pair<UnifiedAddress, diversifier_index_t>>(&addr);
|
||||
if (addrPtr != nullptr) {
|
||||
return addrPtr->first;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<libzcash::UnifiedAddress> UnifiedAddressForReceiver::operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const {
|
||||
auto ufvkPair = wallet.GetUFVKMetadataForReceiver(saplingAddr);
|
||||
if (ufvkPair.has_value()) {
|
||||
|
|
|
@ -1480,7 +1480,7 @@ public:
|
|||
|
||||
//! Generate the unified spending key from the wallet's mnemonic seed
|
||||
//! for the next unused account identifier.
|
||||
std::pair<libzcash::ZcashdUnifiedSpendingKey, libzcash::AccountId>
|
||||
std::pair<libzcash::UnifiedFullViewingKey, libzcash::AccountId>
|
||||
GenerateNewUnifiedSpendingKey();
|
||||
|
||||
//! Generate the unified spending key for the specified ZIP-32/BIP-44
|
||||
|
@ -1816,6 +1816,7 @@ public:
|
|||
// Shielded key and address generalizations
|
||||
//
|
||||
|
||||
// PaymentAddressBelongsToWallet visitor :: (CWallet&, PaymentAddress) -> bool
|
||||
class PaymentAddressBelongsToWallet
|
||||
{
|
||||
private:
|
||||
|
@ -1830,6 +1831,7 @@ public:
|
|||
bool operator()(const libzcash::UnifiedAddress &uaddr) const;
|
||||
};
|
||||
|
||||
// GetViewingKeyForPaymentAddress visitor :: (CWallet&, PaymentAddress) -> std::optional<ViewingKey>
|
||||
class GetViewingKeyForPaymentAddress
|
||||
{
|
||||
private:
|
||||
|
@ -1853,6 +1855,7 @@ enum class PaymentAddressSource {
|
|||
AddressNotFound,
|
||||
};
|
||||
|
||||
// GetSourceForPaymentAddress visitor :: (CWallet&, PaymentAddress) -> PaymentAddressSource
|
||||
class GetSourceForPaymentAddress
|
||||
{
|
||||
private:
|
||||
|
@ -1876,6 +1879,7 @@ enum KeyAddResult {
|
|||
KeyNotAdded,
|
||||
};
|
||||
|
||||
// AddViewingKeyToWallet visitor :: (CWallet&, ViewingKey) -> KeyAddResult
|
||||
class AddViewingKeyToWallet
|
||||
{
|
||||
private:
|
||||
|
@ -1889,6 +1893,8 @@ public:
|
|||
KeyAddResult operator()(const libzcash::UnifiedFullViewingKey &sk) const;
|
||||
};
|
||||
|
||||
// AddSpendingKeyToWallet visitor ::
|
||||
// (CWallet&, Consensus::Params, ..., ViewingKey) -> KeyAddResult
|
||||
class AddSpendingKeyToWallet
|
||||
{
|
||||
private:
|
||||
|
@ -1917,6 +1923,7 @@ public:
|
|||
KeyAddResult operator()(const libzcash::SaplingExtendedSpendingKey &sk) const;
|
||||
};
|
||||
|
||||
// UFVKForReceiver :: (CWallet&, Receiver) -> std::optional<ZcashdUnifiedFullViewingKey>
|
||||
class UFVKForReceiver {
|
||||
private:
|
||||
const CWallet& wallet;
|
||||
|
@ -1924,12 +1931,14 @@ private:
|
|||
public:
|
||||
UFVKForReceiver(const CWallet& wallet): wallet(wallet) {}
|
||||
|
||||
std::optional<libzcash::ZcashdUnifiedFullViewingKey> operator()(const libzcash::OrchardRawAddress& orchardAddr) const;
|
||||
std::optional<libzcash::ZcashdUnifiedFullViewingKey> operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const;
|
||||
std::optional<libzcash::ZcashdUnifiedFullViewingKey> operator()(const CScriptID& scriptId) const;
|
||||
std::optional<libzcash::ZcashdUnifiedFullViewingKey> operator()(const CKeyID& keyId) const;
|
||||
std::optional<libzcash::ZcashdUnifiedFullViewingKey> operator()(const libzcash::UnknownReceiver& receiver) const;
|
||||
};
|
||||
|
||||
// UnifiedAddressForReceiver :: (CWallet&, Receiver) -> std::optional<UnifiedAddress>
|
||||
class UnifiedAddressForReceiver {
|
||||
private:
|
||||
const CWallet& wallet;
|
||||
|
@ -1937,6 +1946,7 @@ private:
|
|||
public:
|
||||
UnifiedAddressForReceiver(const CWallet& wallet): wallet(wallet) {}
|
||||
|
||||
std::optional<libzcash::UnifiedAddress> operator()(const libzcash::OrchardRawAddress& orchardAddr) const;
|
||||
std::optional<libzcash::UnifiedAddress> operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const;
|
||||
std::optional<libzcash::UnifiedAddress> operator()(const CScriptID& scriptId) const;
|
||||
std::optional<libzcash::UnifiedAddress> operator()(const CKeyID& keyId) const;
|
||||
|
|
|
@ -73,12 +73,23 @@ std::optional<SaplingPaymentAddress> UnifiedAddress::GetSaplingReceiver() const
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<OrchardRawAddress> UnifiedAddress::GetOrchardReceiver() const {
|
||||
for (const auto& r : receivers) {
|
||||
if (std::holds_alternative<OrchardRawAddress>(r)) {
|
||||
return std::get<OrchardRawAddress>(r);
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<RecipientAddress> UnifiedAddress::GetPreferredRecipientAddress() const {
|
||||
// Return the first receiver type we recognize; receivers are sorted in
|
||||
// order from most-preferred to least.
|
||||
std::optional<RecipientAddress> result;
|
||||
for (const auto& receiver : *this) {
|
||||
std::visit(match {
|
||||
[&](const OrchardRawAddress& addr) { /* TODO: Return once we enable Orchard as recipient */ },
|
||||
[&](const SaplingPaymentAddress& addr) { result = addr; },
|
||||
[&](const CScriptID& addr) { result = addr; },
|
||||
[&](const CKeyID& addr) { result = addr; },
|
||||
|
@ -94,6 +105,7 @@ std::optional<RecipientAddress> UnifiedAddress::GetPreferredRecipientAddress() c
|
|||
|
||||
bool HasKnownReceiverType(const Receiver& receiver) {
|
||||
return std::visit(match {
|
||||
[](const OrchardRawAddress& addr) { return true; },
|
||||
[](const SaplingPaymentAddress& addr) { return true; },
|
||||
[](const CScriptID& addr) { return true; },
|
||||
[](const CKeyID& addr) { return true; },
|
||||
|
@ -125,6 +137,12 @@ std::pair<std::string, PaymentAddress> AddressInfoFromViewingKey::operator()(con
|
|||
|
||||
} // namespace libzcash
|
||||
|
||||
uint32_t TypecodeForReceiver::operator()(
|
||||
const libzcash::OrchardRawAddress &zaddr) const
|
||||
{
|
||||
return static_cast<uint32_t>(libzcash::ReceiverType::Orchard);
|
||||
}
|
||||
|
||||
uint32_t TypecodeForReceiver::operator()(
|
||||
const libzcash::SaplingPaymentAddress &zaddr) const
|
||||
{
|
||||
|
@ -174,6 +192,16 @@ std::string libzcash::UnifiedFullViewingKey::Encode(const KeyConstants& keyConst
|
|||
return res;
|
||||
}
|
||||
|
||||
std::optional<libzcash::OrchardFullViewingKey> libzcash::UnifiedFullViewingKey::GetOrchardKey() const {
|
||||
std::vector<uint8_t> buffer(96);
|
||||
if (unified_full_viewing_key_read_orchard(inner.get(), buffer.data())) {
|
||||
CDataStream ss(buffer, SER_NETWORK, PROTOCOL_VERSION);
|
||||
return OrchardFullViewingKey::Read(ss);
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<libzcash::SaplingDiversifiableFullViewingKey> libzcash::UnifiedFullViewingKey::GetSaplingKey() const {
|
||||
std::vector<uint8_t> buffer(128);
|
||||
if (unified_full_viewing_key_read_sapling(inner.get(), buffer.data())) {
|
||||
|
@ -214,10 +242,21 @@ bool libzcash::UnifiedFullViewingKeyBuilder::AddSaplingKey(const SaplingDiversif
|
|||
return true;
|
||||
}
|
||||
|
||||
bool libzcash::UnifiedFullViewingKeyBuilder::AddOrchardKey(const OrchardFullViewingKey& key) {
|
||||
if (orchard_bytes.has_value()) return false;
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << key;
|
||||
assert(ss.size() == 96);
|
||||
std::vector<uint8_t> ss_bytes(ss.begin(), ss.end());
|
||||
orchard_bytes = ss_bytes;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<libzcash::UnifiedFullViewingKey> libzcash::UnifiedFullViewingKeyBuilder::build() const {
|
||||
UnifiedFullViewingKeyPtr* ptr = unified_full_viewing_key_from_components(
|
||||
t_bytes.has_value() ? t_bytes.value().data() : nullptr,
|
||||
sapling_bytes.has_value() ? sapling_bytes.value().data() : nullptr);
|
||||
sapling_bytes.has_value() ? sapling_bytes.value().data() : nullptr,
|
||||
orchard_bytes.has_value() ? orchard_bytes.value().data() : nullptr);
|
||||
|
||||
if (ptr == nullptr) {
|
||||
return std::nullopt;
|
||||
|
@ -234,6 +273,9 @@ libzcash::UnifiedFullViewingKey libzcash::UnifiedFullViewingKey::FromZcashdUFVK(
|
|||
if (key.GetSaplingKey().has_value()) {
|
||||
builder.AddSaplingKey(key.GetSaplingKey().value());
|
||||
}
|
||||
if (key.GetOrchardKey().has_value()) {
|
||||
builder.AddOrchardKey(key.GetOrchardKey().value());
|
||||
}
|
||||
|
||||
auto result = builder.build();
|
||||
if (!result.has_value()) {
|
||||
|
|
|
@ -84,6 +84,9 @@ public:
|
|||
std::set<ReceiverType> result;
|
||||
for (const auto& receiver : receivers) {
|
||||
std::visit(match {
|
||||
[&](const libzcash::OrchardRawAddress &zaddr) {
|
||||
result.insert(ReceiverType::Orchard);
|
||||
},
|
||||
[&](const libzcash::SaplingPaymentAddress &zaddr) {
|
||||
result.insert(ReceiverType::Sapling);
|
||||
},
|
||||
|
@ -116,6 +119,8 @@ public:
|
|||
|
||||
std::optional<SaplingPaymentAddress> GetSaplingReceiver() const;
|
||||
|
||||
std::optional<OrchardRawAddress> GetOrchardReceiver() const;
|
||||
|
||||
/**
|
||||
* Return the most-preferred receiver from among the receiver types
|
||||
* that we recognize.
|
||||
|
@ -171,6 +176,8 @@ public:
|
|||
|
||||
std::string Encode(const KeyConstants& keyConstants) const;
|
||||
|
||||
std::optional<OrchardFullViewingKey> GetOrchardKey() const;
|
||||
|
||||
std::optional<SaplingDiversifiableFullViewingKey> GetSaplingKey() const;
|
||||
|
||||
std::optional<transparent::AccountPubKey> GetTransparentKey() const;
|
||||
|
@ -185,6 +192,9 @@ public:
|
|||
if (GetSaplingKey().has_value()) {
|
||||
result.insert(ReceiverType::Sapling);
|
||||
}
|
||||
if (GetOrchardKey().has_value()) {
|
||||
result.insert(ReceiverType::Orchard);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -209,11 +219,16 @@ class UnifiedFullViewingKeyBuilder {
|
|||
private:
|
||||
std::optional<std::vector<uint8_t>> t_bytes;
|
||||
std::optional<std::vector<uint8_t>> sapling_bytes;
|
||||
std::optional<std::vector<uint8_t>> orchard_bytes;
|
||||
public:
|
||||
UnifiedFullViewingKeyBuilder(): t_bytes(std::nullopt), sapling_bytes(std::nullopt) {}
|
||||
UnifiedFullViewingKeyBuilder():
|
||||
t_bytes(std::nullopt),
|
||||
sapling_bytes(std::nullopt),
|
||||
orchard_bytes(std::nullopt) {}
|
||||
|
||||
bool AddTransparentKey(const transparent::AccountPubKey&);
|
||||
bool AddSaplingKey(const SaplingDiversifiableFullViewingKey&);
|
||||
bool AddOrchardKey(const OrchardFullViewingKey&);
|
||||
|
||||
std::optional<UnifiedFullViewingKey> build() const;
|
||||
};
|
||||
|
@ -283,6 +298,7 @@ class TypecodeForReceiver {
|
|||
public:
|
||||
TypecodeForReceiver() {}
|
||||
|
||||
uint32_t operator()(const libzcash::OrchardRawAddress &zaddr) const;
|
||||
uint32_t operator()(const libzcash::SaplingPaymentAddress &zaddr) const;
|
||||
uint32_t operator()(const CScriptID &p2sh) const;
|
||||
uint32_t operator()(const CKeyID &p2pkh) const;
|
||||
|
|
|
@ -10,10 +10,23 @@ OrchardRawAddress OrchardIncomingViewingKey::Address(const diversifier_index_t&
|
|||
return OrchardRawAddress(orchard_incoming_viewing_key_to_address(inner.get(), j.begin()));
|
||||
}
|
||||
|
||||
std::optional<diversifier_index_t> OrchardIncomingViewingKey::DecryptDiversifier(const OrchardRawAddress& addr) const {
|
||||
diversifier_index_t j_ret;
|
||||
if (orchard_incoming_viewing_key_decrypt_diversifier(inner.get(), addr.inner.get(), j_ret.begin())) {
|
||||
return j_ret;
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
OrchardIncomingViewingKey OrchardFullViewingKey::ToIncomingViewingKey() const {
|
||||
return OrchardIncomingViewingKey(orchard_full_viewing_key_to_incoming_viewing_key(inner.get()));
|
||||
}
|
||||
|
||||
OrchardIncomingViewingKey OrchardFullViewingKey::ToInternalIncomingViewingKey() const {
|
||||
return OrchardIncomingViewingKey(orchard_full_viewing_key_to_incoming_viewing_key(inner.get()));
|
||||
}
|
||||
|
||||
OrchardSpendingKey OrchardSpendingKey::ForAccount(
|
||||
const HDSeed& seed,
|
||||
uint32_t bip44CoinType,
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
#include "zcash/address/zip32.h"
|
||||
#include <rust/orchard/keys.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
class OrchardWallet;
|
||||
namespace orchard { class Builder; }
|
||||
|
||||
|
@ -31,6 +33,10 @@ private:
|
|||
friend class ::OrchardWallet;
|
||||
friend class ::orchard::Builder;
|
||||
public:
|
||||
static OrchardRawAddress KeyIoOnlyFromReceiver(OrchardRawAddressPtr* ptr) {
|
||||
return OrchardRawAddress(ptr);
|
||||
}
|
||||
|
||||
OrchardRawAddress(OrchardRawAddress&& key) : inner(std::move(key.inner)) {}
|
||||
|
||||
OrchardRawAddress(const OrchardRawAddress& key) :
|
||||
|
@ -52,9 +58,38 @@ public:
|
|||
return *this;
|
||||
}
|
||||
|
||||
friend bool operator==(const OrchardRawAddress& c1, const OrchardRawAddress& c2) {
|
||||
return orchard_address_eq(c1.inner.get(), c2.inner.get());
|
||||
}
|
||||
|
||||
friend bool operator<(const OrchardRawAddress& c1, const OrchardRawAddress& c2) {
|
||||
return orchard_address_lt(c1.inner.get(), c2.inner.get());
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
void Serialize(Stream& s) const {
|
||||
RustStream rs(s);
|
||||
if (!orchard_raw_address_serialize(inner.get(), &rs, RustStream<Stream>::write_callback)) {
|
||||
throw std::ios_base::failure("Failed to serialize Orchard raw address to bytes");
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
void Unserialize(Stream& s) {
|
||||
RustStream rs(s);
|
||||
OrchardRawAddressPtr* addr = orchard_raw_address_parse(&rs, RustStream<Stream>::read_callback);
|
||||
if (addr == nullptr) {
|
||||
throw std::ios_base::failure("Failed to parse Orchard raw address bytes");
|
||||
}
|
||||
inner.reset(addr);
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
static OrchardRawAddress Read(Stream& stream) {
|
||||
OrchardRawAddress key;
|
||||
stream >> key;
|
||||
return key;
|
||||
}
|
||||
};
|
||||
|
||||
class OrchardIncomingViewingKey
|
||||
|
@ -80,6 +115,12 @@ public:
|
|||
|
||||
OrchardRawAddress Address(const diversifier_index_t& j) const;
|
||||
|
||||
/**
|
||||
* Decrypts the diversifier for the given raw address, and returns it if that
|
||||
* address was derived from this IVK; otherwise returns std::nullopt;
|
||||
*/
|
||||
std::optional<diversifier_index_t> DecryptDiversifier(const OrchardRawAddress& addr) const;
|
||||
|
||||
OrchardIncomingViewingKey& operator=(OrchardIncomingViewingKey&& key)
|
||||
{
|
||||
if (this != &key) {
|
||||
|
@ -151,6 +192,8 @@ public:
|
|||
|
||||
OrchardIncomingViewingKey ToIncomingViewingKey() const;
|
||||
|
||||
OrchardIncomingViewingKey ToInternalIncomingViewingKey() const;
|
||||
|
||||
OrchardFullViewingKey& operator=(OrchardFullViewingKey&& key)
|
||||
{
|
||||
if (this != &key) {
|
||||
|
@ -237,31 +280,6 @@ public:
|
|||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
void Serialize(Stream& s) const {
|
||||
RustStream rs(s);
|
||||
if (!orchard_spending_key_serialize(inner.get(), &rs, RustStream<Stream>::write_callback)) {
|
||||
throw std::ios_base::failure("Failed to serialize Orchard spending key");
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
void Unserialize(Stream& s) {
|
||||
RustStream rs(s);
|
||||
OrchardSpendingKeyPtr* key = orchard_spending_key_parse(&rs, RustStream<Stream>::read_callback);
|
||||
if (key == nullptr) {
|
||||
throw std::ios_base::failure("Failed to parse Orchard spending key");
|
||||
}
|
||||
inner.reset(key);
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
static OrchardSpendingKey Read(Stream& stream) {
|
||||
OrchardSpendingKey key;
|
||||
stream >> key;
|
||||
return key;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace libzcash
|
||||
|
|
|
@ -16,7 +16,7 @@ using namespace libzcash;
|
|||
bool libzcash::HasShielded(const std::set<ReceiverType>& receiverTypes) {
|
||||
auto has_shielded = [](ReceiverType r) {
|
||||
// TODO: update this as support for new shielded protocols is added.
|
||||
return r == ReceiverType::Sapling;
|
||||
return r == ReceiverType::Sapling || r == ReceiverType::Orchard;
|
||||
};
|
||||
return std::find_if(receiverTypes.begin(), receiverTypes.end(), has_shielded) != receiverTypes.end();
|
||||
}
|
||||
|
@ -39,7 +39,9 @@ std::optional<ZcashdUnifiedSpendingKey> ZcashdUnifiedSpendingKey::ForAccount(
|
|||
|
||||
auto saplingKey = SaplingExtendedSpendingKey::ForAccount(seed, bip44CoinType, accountId);
|
||||
|
||||
return ZcashdUnifiedSpendingKey(transparentKey.value(), saplingKey.first);
|
||||
auto orchardKey = OrchardSpendingKey::ForAccount(seed, bip44CoinType, accountId);
|
||||
|
||||
return ZcashdUnifiedSpendingKey(transparentKey.value(), saplingKey.first, orchardKey);
|
||||
}
|
||||
|
||||
UnifiedFullViewingKey ZcashdUnifiedSpendingKey::ToFullViewingKey() const {
|
||||
|
@ -47,6 +49,7 @@ UnifiedFullViewingKey ZcashdUnifiedSpendingKey::ToFullViewingKey() const {
|
|||
|
||||
builder.AddTransparentKey(transparentKey.ToAccountPubKey());
|
||||
builder.AddSaplingKey(saplingKey.ToXFVK());
|
||||
builder.AddOrchardKey(orchardKey.ToFullViewingKey());
|
||||
|
||||
// This call to .value() is safe as ZcashdUnifiedSpendingKey values are always
|
||||
// constructed to contain all required components.
|
||||
|
@ -69,6 +72,11 @@ ZcashdUnifiedFullViewingKey ZcashdUnifiedFullViewingKey::FromUnifiedFullViewingK
|
|||
result.saplingKey = saplingKey.value();
|
||||
}
|
||||
|
||||
auto orchardKey = ufvk.GetOrchardKey();
|
||||
if (orchardKey.has_value()) {
|
||||
result.orchardKey = orchardKey.value();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -85,6 +93,14 @@ UnifiedAddressGenerationResult ZcashdUnifiedFullViewingKey::Address(
|
|||
}
|
||||
|
||||
UnifiedAddress ua;
|
||||
if (receiverTypes.count(ReceiverType::Orchard) > 0) {
|
||||
if (orchardKey.has_value()) {
|
||||
ua.AddReceiver(orchardKey.value().ToIncomingViewingKey().Address(j));
|
||||
} else {
|
||||
return UnifiedAddressGenerationError::ReceiverTypeNotAvailable;
|
||||
}
|
||||
}
|
||||
|
||||
if (receiverTypes.count(ReceiverType::Sapling) > 0) {
|
||||
if (saplingKey.has_value()) {
|
||||
auto saplingAddress = saplingKey.value().Address(j);
|
||||
|
@ -180,6 +196,9 @@ UnifiedFullViewingKey ZcashdUnifiedFullViewingKey::ToFullViewingKey() const {
|
|||
if (saplingKey.has_value()) {
|
||||
builder.AddSaplingKey(saplingKey.value());
|
||||
}
|
||||
if (orchardKey.has_value()) {
|
||||
builder.AddOrchardKey(orchardKey.value());
|
||||
}
|
||||
|
||||
// This call to .value() is safe as ZcashdUnifiedFullViewingKey values are always
|
||||
// constructed to contain all required components.
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "transparent.h"
|
||||
#include "key_constants.h"
|
||||
#include "script/script.h"
|
||||
#include "zcash/address/orchard.hpp"
|
||||
#include "zip32.h"
|
||||
|
||||
#include <variant>
|
||||
|
@ -23,7 +24,7 @@ enum class ReceiverType: uint32_t {
|
|||
P2PKH = 0x00,
|
||||
P2SH = 0x01,
|
||||
Sapling = 0x02,
|
||||
//Orchard = 0x03
|
||||
Orchard = 0x03
|
||||
};
|
||||
|
||||
enum class UnifiedAddressGenerationError {
|
||||
|
@ -113,6 +114,7 @@ public:
|
|||
* variants by `operator<` is equivalent to sorting by preference.
|
||||
*/
|
||||
typedef std::variant<
|
||||
OrchardRawAddress,
|
||||
SaplingPaymentAddress,
|
||||
CScriptID,
|
||||
CKeyID,
|
||||
|
@ -139,6 +141,7 @@ private:
|
|||
UFVKId keyId;
|
||||
std::optional<transparent::AccountPubKey> transparentKey;
|
||||
std::optional<SaplingDiversifiableFullViewingKey> saplingKey;
|
||||
std::optional<OrchardFullViewingKey> orchardKey;
|
||||
|
||||
ZcashdUnifiedFullViewingKey() {}
|
||||
|
||||
|
@ -167,6 +170,10 @@ public:
|
|||
return saplingKey;
|
||||
}
|
||||
|
||||
const std::optional<OrchardFullViewingKey>& GetOrchardKey() const {
|
||||
return orchardKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new unified address having the specified receiver types, at the specified
|
||||
* diversifier index, unless the diversifer index would generate an invalid receiver.
|
||||
|
@ -241,10 +248,12 @@ class ZcashdUnifiedSpendingKey {
|
|||
private:
|
||||
transparent::AccountKey transparentKey;
|
||||
SaplingExtendedSpendingKey saplingKey;
|
||||
OrchardSpendingKey orchardKey;
|
||||
|
||||
ZcashdUnifiedSpendingKey(
|
||||
transparent::AccountKey tkey,
|
||||
SaplingExtendedSpendingKey skey): transparentKey(tkey), saplingKey(skey) {}
|
||||
SaplingExtendedSpendingKey skey,
|
||||
OrchardSpendingKey okey): transparentKey(tkey), saplingKey(skey), orchardKey(okey) {}
|
||||
public:
|
||||
static std::optional<ZcashdUnifiedSpendingKey> ForAccount(
|
||||
const HDSeed& seed,
|
||||
|
@ -255,10 +264,14 @@ public:
|
|||
return transparentKey;
|
||||
}
|
||||
|
||||
const SaplingExtendedSpendingKey& GetSaplingExtendedSpendingKey() const {
|
||||
const SaplingExtendedSpendingKey& GetSaplingKey() const {
|
||||
return saplingKey;
|
||||
}
|
||||
|
||||
const OrchardSpendingKey& GetOrchardKey() const {
|
||||
return orchardKey;
|
||||
}
|
||||
|
||||
UnifiedFullViewingKey ToFullViewingKey() const;
|
||||
};
|
||||
|
||||
|
|
|
@ -295,4 +295,4 @@ bool IsInternalKeyPath(uint32_t purpose, uint32_t coinType, const std::string& k
|
|||
}
|
||||
}
|
||||
|
||||
};
|
||||
} //namespace libzcash
|
||||
|
|
Loading…
Reference in New Issue