Add Orchard components to unified full viewing keys
Co-authored-by: Jack Grigg <jack@z.cash>
This commit is contained in:
parent
1a1522a4f1
commit
258f0fc72f
|
@ -8,7 +8,7 @@ replace-with = "vendored-sources"
|
||||||
|
|
||||||
[source."https://github.com/zcash/orchard.git"]
|
[source."https://github.com/zcash/orchard.git"]
|
||||||
git = "https://github.com/zcash/orchard.git"
|
git = "https://github.com/zcash/orchard.git"
|
||||||
rev = "4dc1ae059a59ee911134cb3e731c7be627a71d4d"
|
rev = "3b8d07f7b64b2329622089ac9698e4cce97e2f14"
|
||||||
replace-with = "vendored-sources"
|
replace-with = "vendored-sources"
|
||||||
|
|
||||||
[source."https://github.com/nuttycom/hdwallet.git"]
|
[source."https://github.com/nuttycom/hdwallet.git"]
|
||||||
|
|
|
@ -1090,7 +1090,7 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "orchard"
|
name = "orchard"
|
||||||
version = "0.1.0-beta.1"
|
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 = [
|
dependencies = [
|
||||||
"aes",
|
"aes",
|
||||||
"arrayvec 0.7.2",
|
"arrayvec 0.7.2",
|
||||||
|
|
|
@ -72,7 +72,7 @@ codegen-units = 1
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
hdwallet = { git = "https://github.com/nuttycom/hdwallet", rev = "576683b9f2865f1118c309017ff36e01f84420c9" }
|
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_address = { git = "https://github.com/zcash/librustzcash.git", rev = "3d935a94e75786a67c3ea4992d7c372af203086f" }
|
||||||
zcash_history = { 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" }
|
zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "3d935a94e75786a67c3ea4992d7c372af203086f" }
|
||||||
|
|
|
@ -293,14 +293,13 @@ TEST(Keys, EncodeAndDecodeUnifiedFullViewingKeys)
|
||||||
auto key = libzcash::SaplingDiversifiableFullViewingKey::Read(ss);
|
auto key = libzcash::SaplingDiversifiableFullViewingKey::Read(ss);
|
||||||
ASSERT_TRUE(builder.AddSaplingKey(key));
|
ASSERT_TRUE(builder.AddSaplingKey(key));
|
||||||
}
|
}
|
||||||
|
if (!test[2].isNull()) {
|
||||||
// Orchard keys and unknown items are not yet supported; instead,
|
auto data = ParseHex(test[2].get_str());
|
||||||
// we just test that we're able to parse the unified key string
|
ASSERT_EQ(data.size(), 96);
|
||||||
// and that the constituent items match the elements; if no Sapling
|
CDataStream ss(data, SER_NETWORK, PROTOCOL_VERSION);
|
||||||
// key is present then UFVK construction would fail because it might
|
auto key = libzcash::OrchardFullViewingKey::Read(ss);
|
||||||
// presume the UFVK to be transparent-only.
|
ASSERT_TRUE(builder.AddOrchardKey(key));
|
||||||
if (test[1].isNull())
|
}
|
||||||
continue;
|
|
||||||
|
|
||||||
auto built = builder.build();
|
auto built = builder.build();
|
||||||
ASSERT_TRUE(built.has_value());
|
ASSERT_TRUE(built.has_value());
|
||||||
|
@ -313,5 +312,6 @@ TEST(Keys, EncodeAndDecodeUnifiedFullViewingKeys)
|
||||||
|
|
||||||
EXPECT_EQ(decoded.value().GetTransparentKey(), built.value().GetTransparentKey());
|
EXPECT_EQ(decoded.value().GetTransparentKey(), built.value().GetTransparentKey());
|
||||||
EXPECT_EQ(decoded.value().GetSaplingKey(), built.value().GetSaplingKey());
|
EXPECT_EQ(decoded.value().GetSaplingKey(), built.value().GetSaplingKey());
|
||||||
|
EXPECT_EQ(decoded.value().GetOrchardKey(), built.value().GetOrchardKey());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -312,14 +312,26 @@ bool CBasicKeyStore::AddUnifiedFullViewingKey(
|
||||||
{
|
{
|
||||||
LOCK(cs_KeyStore);
|
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.
|
// Add the Sapling component of the UFVK to the wallet.
|
||||||
auto saplingKey = ufvk.GetSaplingKey();
|
auto saplingKey = ufvk.GetSaplingKey();
|
||||||
if (saplingKey.has_value()) {
|
if (saplingKey.has_value()) {
|
||||||
auto ivk = saplingKey.value().ToIncomingViewingKey();
|
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();
|
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
|
// We can't reasonably add the transparent component here, because
|
||||||
|
@ -329,7 +341,7 @@ bool CBasicKeyStore::AddUnifiedFullViewingKey(
|
||||||
// transparent part of the address must be added to the keystore.
|
// transparent part of the address must be added to the keystore.
|
||||||
|
|
||||||
// Add the UFVK by key identifier.
|
// Add the UFVK by key identifier.
|
||||||
mapUnifiedFullViewingKeys.insert({ufvk.GetKeyID(), ufvk});
|
mapUnifiedFullViewingKeys.insert({ufvkId, ufvk});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -390,6 +402,15 @@ std::optional<libzcash::UFVKId> CBasicKeyStore::GetUFVKIdForViewingKey(const lib
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[&](const libzcash::UnifiedFullViewingKey& ufvk) {
|
[&](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();
|
const auto saplingDfvk = ufvk.GetSaplingKey();
|
||||||
if (saplingDfvk.has_value()) {
|
if (saplingDfvk.has_value()) {
|
||||||
const auto saplingIvk = saplingDfvk.value().ToIncomingViewingKey();
|
const auto saplingIvk = saplingDfvk.value().ToIncomingViewingKey();
|
||||||
|
@ -405,7 +426,15 @@ std::optional<libzcash::UFVKId> CBasicKeyStore::GetUFVKIdForViewingKey(const lib
|
||||||
|
|
||||||
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
|
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
|
||||||
FindUFVKId::operator()(const libzcash::OrchardRawAddress& orchardAddr) const {
|
FindUFVKId::operator()(const libzcash::OrchardRawAddress& orchardAddr) const {
|
||||||
// TODO: Implement once we have Orchard in UFVKs
|
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;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -185,6 +185,7 @@ protected:
|
||||||
std::map<CKeyID, std::pair<libzcash::UFVKId, libzcash::diversifier_index_t>> mapP2PKHUnified;
|
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<CScriptID, std::pair<libzcash::UFVKId, libzcash::diversifier_index_t>> mapP2SHUnified;
|
||||||
std::map<libzcash::SaplingIncomingViewingKey, libzcash::UFVKId> mapSaplingKeyUnified;
|
std::map<libzcash::SaplingIncomingViewingKey, libzcash::UFVKId> mapSaplingKeyUnified;
|
||||||
|
std::map<libzcash::OrchardIncomingViewingKey, libzcash::UFVKId> mapOrchardKeyUnified;
|
||||||
std::map<libzcash::UFVKId, libzcash::ZcashdUnifiedFullViewingKey> mapUnifiedFullViewingKeys;
|
std::map<libzcash::UFVKId, libzcash::ZcashdUnifiedFullViewingKey> mapUnifiedFullViewingKeys;
|
||||||
|
|
||||||
friend class FindUFVKId;
|
friend class FindUFVKId;
|
||||||
|
|
|
@ -97,6 +97,18 @@ OrchardRawAddressPtr* orchard_incoming_viewing_key_to_address(
|
||||||
const OrchardIncomingViewingKeyPtr* incoming_viewing_key,
|
const OrchardIncomingViewingKeyPtr* incoming_viewing_key,
|
||||||
const unsigned char* j);
|
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.
|
* Parses an Orchard incoming viewing key from the given stream.
|
||||||
*
|
*
|
||||||
|
@ -179,6 +191,12 @@ bool orchard_full_viewing_key_serialize(
|
||||||
OrchardIncomingViewingKeyPtr* orchard_full_viewing_key_to_incoming_viewing_key(
|
OrchardIncomingViewingKeyPtr* orchard_full_viewing_key_to_incoming_viewing_key(
|
||||||
const OrchardFullViewingKeyPtr* 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.
|
* Implements equality testing between full viewing keys.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -84,6 +84,22 @@ bool unified_full_viewing_key_read_sapling(
|
||||||
const UnifiedFullViewingKeyPtr* full_viewing_key,
|
const UnifiedFullViewingKeyPtr* full_viewing_key,
|
||||||
unsigned char* skeyout);
|
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
|
* Constructs a unified full viewing key from the binary encodings
|
||||||
* of its constituent parts.
|
* of its constituent parts.
|
||||||
|
@ -101,7 +117,8 @@ bool unified_full_viewing_key_read_sapling(
|
||||||
*/
|
*/
|
||||||
UnifiedFullViewingKeyPtr* unified_full_viewing_key_from_components(
|
UnifiedFullViewingKeyPtr* unified_full_viewing_key_from_components(
|
||||||
const unsigned char* t_key,
|
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
|
* Derive the internal and external OVKs for the binary encoding
|
||||||
|
|
|
@ -145,6 +145,26 @@ pub extern "C" fn orchard_incoming_viewing_key_to_address(
|
||||||
Box::into_raw(Box::new(key.address_at(diversifier_index)))
|
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]
|
#[no_mangle]
|
||||||
pub extern "C" fn orchard_incoming_viewing_key_serialize(
|
pub extern "C" fn orchard_incoming_viewing_key_serialize(
|
||||||
key: *const IncomingViewingKey,
|
key: *const IncomingViewingKey,
|
||||||
|
@ -250,6 +270,18 @@ pub extern "C" fn orchard_full_viewing_key_to_incoming_viewing_key(
|
||||||
.unwrap_or(std::ptr::null_mut())
|
.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]
|
#[no_mangle]
|
||||||
pub extern "C" fn orchard_full_viewing_key_eq(
|
pub extern "C" fn orchard_full_viewing_key_eq(
|
||||||
k0: *const FullViewingKey,
|
k0: *const FullViewingKey,
|
||||||
|
|
|
@ -139,13 +139,33 @@ pub extern "C" fn unified_full_viewing_key_read_sapling(
|
||||||
false
|
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]
|
#[no_mangle]
|
||||||
pub extern "C" fn unified_full_viewing_key_from_components(
|
pub extern "C" fn unified_full_viewing_key_from_components(
|
||||||
t_key: *const [u8; 65],
|
t_key: *const [u8; 65],
|
||||||
sapling_key: *const [u8; 128],
|
sapling_key: *const [u8; 128],
|
||||||
|
orchard_key: *const [u8; 96],
|
||||||
) -> *mut Ufvk {
|
) -> *mut Ufvk {
|
||||||
let t_key = unsafe { t_key.as_ref() };
|
let t_key = unsafe { t_key.as_ref() };
|
||||||
let sapling_key = unsafe { sapling_key.as_ref() };
|
let sapling_key = unsafe { sapling_key.as_ref() };
|
||||||
|
let orchard_key = unsafe { orchard_key.as_ref() };
|
||||||
|
|
||||||
let mut items = vec![];
|
let mut items = vec![];
|
||||||
if let Some(t_bytes) = t_key {
|
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 {
|
if let Some(sapling_bytes) = sapling_key {
|
||||||
items.push(Fvk::Sapling(*sapling_bytes));
|
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) {
|
match Ufvk::try_from_items(items) {
|
||||||
Ok(ufvk) => Box::into_raw(Box::new(ufvk)),
|
Ok(ufvk) => Box::into_raw(Box::new(ufvk)),
|
||||||
|
|
|
@ -6556,8 +6556,18 @@ KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SaplingExtendedS
|
||||||
// UFVKForReceiver :: (CWallet&, Receiver) -> std::optional<ZcashdUnifiedFullViewingKey>
|
// UFVKForReceiver :: (CWallet&, Receiver) -> std::optional<ZcashdUnifiedFullViewingKey>
|
||||||
|
|
||||||
std::optional<libzcash::ZcashdUnifiedFullViewingKey> UFVKForReceiver::operator()(const libzcash::OrchardRawAddress& orchardAddr) const {
|
std::optional<libzcash::ZcashdUnifiedFullViewingKey> UFVKForReceiver::operator()(const libzcash::OrchardRawAddress& orchardAddr) const {
|
||||||
// TODO: Implement once we have Orchard in UFVKs
|
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;
|
return std::nullopt;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
std::optional<libzcash::ZcashdUnifiedFullViewingKey> UFVKForReceiver::operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const {
|
std::optional<libzcash::ZcashdUnifiedFullViewingKey> UFVKForReceiver::operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const {
|
||||||
auto ufvkPair = wallet.GetUFVKMetadataForReceiver(saplingAddr);
|
auto ufvkPair = wallet.GetUFVKMetadataForReceiver(saplingAddr);
|
||||||
|
@ -6600,7 +6610,30 @@ std::optional<libzcash::ZcashdUnifiedFullViewingKey> UFVKForReceiver::operator()
|
||||||
|
|
||||||
std::optional<libzcash::UnifiedAddress> UnifiedAddressForReceiver::operator()(
|
std::optional<libzcash::UnifiedAddress> UnifiedAddressForReceiver::operator()(
|
||||||
const libzcash::OrchardRawAddress& orchardAddr) const {
|
const libzcash::OrchardRawAddress& orchardAddr) const {
|
||||||
// TODO: Implement once we have Orchard in UFVKs
|
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;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -192,6 +192,16 @@ std::string libzcash::UnifiedFullViewingKey::Encode(const KeyConstants& keyConst
|
||||||
return res;
|
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::optional<libzcash::SaplingDiversifiableFullViewingKey> libzcash::UnifiedFullViewingKey::GetSaplingKey() const {
|
||||||
std::vector<uint8_t> buffer(128);
|
std::vector<uint8_t> buffer(128);
|
||||||
if (unified_full_viewing_key_read_sapling(inner.get(), buffer.data())) {
|
if (unified_full_viewing_key_read_sapling(inner.get(), buffer.data())) {
|
||||||
|
@ -232,10 +242,21 @@ bool libzcash::UnifiedFullViewingKeyBuilder::AddSaplingKey(const SaplingDiversif
|
||||||
return true;
|
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 {
|
std::optional<libzcash::UnifiedFullViewingKey> libzcash::UnifiedFullViewingKeyBuilder::build() const {
|
||||||
UnifiedFullViewingKeyPtr* ptr = unified_full_viewing_key_from_components(
|
UnifiedFullViewingKeyPtr* ptr = unified_full_viewing_key_from_components(
|
||||||
t_bytes.has_value() ? t_bytes.value().data() : nullptr,
|
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) {
|
if (ptr == nullptr) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
@ -252,6 +273,9 @@ libzcash::UnifiedFullViewingKey libzcash::UnifiedFullViewingKey::FromZcashdUFVK(
|
||||||
if (key.GetSaplingKey().has_value()) {
|
if (key.GetSaplingKey().has_value()) {
|
||||||
builder.AddSaplingKey(key.GetSaplingKey().value());
|
builder.AddSaplingKey(key.GetSaplingKey().value());
|
||||||
}
|
}
|
||||||
|
if (key.GetOrchardKey().has_value()) {
|
||||||
|
builder.AddOrchardKey(key.GetOrchardKey().value());
|
||||||
|
}
|
||||||
|
|
||||||
auto result = builder.build();
|
auto result = builder.build();
|
||||||
if (!result.has_value()) {
|
if (!result.has_value()) {
|
||||||
|
|
|
@ -176,6 +176,8 @@ public:
|
||||||
|
|
||||||
std::string Encode(const KeyConstants& keyConstants) const;
|
std::string Encode(const KeyConstants& keyConstants) const;
|
||||||
|
|
||||||
|
std::optional<OrchardFullViewingKey> GetOrchardKey() const;
|
||||||
|
|
||||||
std::optional<SaplingDiversifiableFullViewingKey> GetSaplingKey() const;
|
std::optional<SaplingDiversifiableFullViewingKey> GetSaplingKey() const;
|
||||||
|
|
||||||
std::optional<transparent::AccountPubKey> GetTransparentKey() const;
|
std::optional<transparent::AccountPubKey> GetTransparentKey() const;
|
||||||
|
@ -190,6 +192,9 @@ public:
|
||||||
if (GetSaplingKey().has_value()) {
|
if (GetSaplingKey().has_value()) {
|
||||||
result.insert(ReceiverType::Sapling);
|
result.insert(ReceiverType::Sapling);
|
||||||
}
|
}
|
||||||
|
if (GetOrchardKey().has_value()) {
|
||||||
|
result.insert(ReceiverType::Orchard);
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,11 +219,16 @@ class UnifiedFullViewingKeyBuilder {
|
||||||
private:
|
private:
|
||||||
std::optional<std::vector<uint8_t>> t_bytes;
|
std::optional<std::vector<uint8_t>> t_bytes;
|
||||||
std::optional<std::vector<uint8_t>> sapling_bytes;
|
std::optional<std::vector<uint8_t>> sapling_bytes;
|
||||||
|
std::optional<std::vector<uint8_t>> orchard_bytes;
|
||||||
public:
|
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 AddTransparentKey(const transparent::AccountPubKey&);
|
||||||
bool AddSaplingKey(const SaplingDiversifiableFullViewingKey&);
|
bool AddSaplingKey(const SaplingDiversifiableFullViewingKey&);
|
||||||
|
bool AddOrchardKey(const OrchardFullViewingKey&);
|
||||||
|
|
||||||
std::optional<UnifiedFullViewingKey> build() const;
|
std::optional<UnifiedFullViewingKey> build() const;
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,10 +10,23 @@ OrchardRawAddress OrchardIncomingViewingKey::Address(const diversifier_index_t&
|
||||||
return OrchardRawAddress(orchard_incoming_viewing_key_to_address(inner.get(), j.begin()));
|
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 {
|
OrchardIncomingViewingKey OrchardFullViewingKey::ToIncomingViewingKey() const {
|
||||||
return OrchardIncomingViewingKey(orchard_full_viewing_key_to_incoming_viewing_key(inner.get()));
|
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(
|
OrchardSpendingKey OrchardSpendingKey::ForAccount(
|
||||||
const HDSeed& seed,
|
const HDSeed& seed,
|
||||||
uint32_t bip44CoinType,
|
uint32_t bip44CoinType,
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
#include "zcash/address/zip32.h"
|
#include "zcash/address/zip32.h"
|
||||||
#include <rust/orchard/keys.h>
|
#include <rust/orchard/keys.h>
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
class OrchardWallet;
|
class OrchardWallet;
|
||||||
namespace orchard { class Builder; }
|
namespace orchard { class Builder; }
|
||||||
|
|
||||||
|
@ -113,6 +115,12 @@ public:
|
||||||
|
|
||||||
OrchardRawAddress Address(const diversifier_index_t& j) const;
|
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)
|
OrchardIncomingViewingKey& operator=(OrchardIncomingViewingKey&& key)
|
||||||
{
|
{
|
||||||
if (this != &key) {
|
if (this != &key) {
|
||||||
|
@ -184,6 +192,8 @@ public:
|
||||||
|
|
||||||
OrchardIncomingViewingKey ToIncomingViewingKey() const;
|
OrchardIncomingViewingKey ToIncomingViewingKey() const;
|
||||||
|
|
||||||
|
OrchardIncomingViewingKey ToInternalIncomingViewingKey() const;
|
||||||
|
|
||||||
OrchardFullViewingKey& operator=(OrchardFullViewingKey&& key)
|
OrchardFullViewingKey& operator=(OrchardFullViewingKey&& key)
|
||||||
{
|
{
|
||||||
if (this != &key) {
|
if (this != &key) {
|
||||||
|
|
|
@ -16,7 +16,7 @@ using namespace libzcash;
|
||||||
bool libzcash::HasShielded(const std::set<ReceiverType>& receiverTypes) {
|
bool libzcash::HasShielded(const std::set<ReceiverType>& receiverTypes) {
|
||||||
auto has_shielded = [](ReceiverType r) {
|
auto has_shielded = [](ReceiverType r) {
|
||||||
// TODO: update this as support for new shielded protocols is added.
|
// 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();
|
return std::find_if(receiverTypes.begin(), receiverTypes.end(), has_shielded) != receiverTypes.end();
|
||||||
}
|
}
|
||||||
|
@ -69,6 +69,11 @@ ZcashdUnifiedFullViewingKey ZcashdUnifiedFullViewingKey::FromUnifiedFullViewingK
|
||||||
result.saplingKey = saplingKey.value();
|
result.saplingKey = saplingKey.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto orchardKey = ufvk.GetOrchardKey();
|
||||||
|
if (orchardKey.has_value()) {
|
||||||
|
result.orchardKey = orchardKey.value();
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,6 +90,14 @@ UnifiedAddressGenerationResult ZcashdUnifiedFullViewingKey::Address(
|
||||||
}
|
}
|
||||||
|
|
||||||
UnifiedAddress ua;
|
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 (receiverTypes.count(ReceiverType::Sapling) > 0) {
|
||||||
if (saplingKey.has_value()) {
|
if (saplingKey.has_value()) {
|
||||||
auto saplingAddress = saplingKey.value().Address(j);
|
auto saplingAddress = saplingKey.value().Address(j);
|
||||||
|
@ -180,6 +193,9 @@ UnifiedFullViewingKey ZcashdUnifiedFullViewingKey::ToFullViewingKey() const {
|
||||||
if (saplingKey.has_value()) {
|
if (saplingKey.has_value()) {
|
||||||
builder.AddSaplingKey(saplingKey.value());
|
builder.AddSaplingKey(saplingKey.value());
|
||||||
}
|
}
|
||||||
|
if (orchardKey.has_value()) {
|
||||||
|
builder.AddOrchardKey(orchardKey.value());
|
||||||
|
}
|
||||||
|
|
||||||
// This call to .value() is safe as ZcashdUnifiedFullViewingKey values are always
|
// This call to .value() is safe as ZcashdUnifiedFullViewingKey values are always
|
||||||
// constructed to contain all required components.
|
// constructed to contain all required components.
|
||||||
|
|
|
@ -141,6 +141,7 @@ private:
|
||||||
UFVKId keyId;
|
UFVKId keyId;
|
||||||
std::optional<transparent::AccountPubKey> transparentKey;
|
std::optional<transparent::AccountPubKey> transparentKey;
|
||||||
std::optional<SaplingDiversifiableFullViewingKey> saplingKey;
|
std::optional<SaplingDiversifiableFullViewingKey> saplingKey;
|
||||||
|
std::optional<OrchardFullViewingKey> orchardKey;
|
||||||
|
|
||||||
ZcashdUnifiedFullViewingKey() {}
|
ZcashdUnifiedFullViewingKey() {}
|
||||||
|
|
||||||
|
@ -169,6 +170,10 @@ public:
|
||||||
return saplingKey;
|
return saplingKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::optional<OrchardFullViewingKey>& GetOrchardKey() const {
|
||||||
|
return orchardKey;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new unified address having the specified receiver types, at the specified
|
* Creates a new unified address having the specified receiver types, at the specified
|
||||||
* diversifier index, unless the diversifer index would generate an invalid receiver.
|
* diversifier index, unless the diversifer index would generate an invalid receiver.
|
||||||
|
|
Loading…
Reference in New Issue