Merge pull request #5400 from nuttycom/feature/wallet_orchard-ufvks

Add types & FFI for serialization of unified full viewing keys.
This commit is contained in:
str4d 2021-12-14 21:38:53 +00:00 committed by GitHub
commit 87eeee0607
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 775 additions and 111 deletions

16
Cargo.lock generated
View File

@ -521,7 +521,7 @@ checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
[[package]]
name = "equihash"
version = "0.1.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=0ec7f97c976d55e1a194a37b27f247e8887fca1d#0ec7f97c976d55e1a194a37b27f247e8887fca1d"
source = "git+https://github.com/zcash/librustzcash.git?rev=69c3b4b5e143bb349cca76c677b7cd0a8556b28f#69c3b4b5e143bb349cca76c677b7cd0a8556b28f"
dependencies = [
"blake2b_simd",
"byteorder",
@ -530,7 +530,7 @@ dependencies = [
[[package]]
name = "f4jumble"
version = "0.0.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=0ec7f97c976d55e1a194a37b27f247e8887fca1d#0ec7f97c976d55e1a194a37b27f247e8887fca1d"
source = "git+https://github.com/zcash/librustzcash.git?rev=69c3b4b5e143bb349cca76c677b7cd0a8556b28f#69c3b4b5e143bb349cca76c677b7cd0a8556b28f"
dependencies = [
"blake2b_simd",
]
@ -1885,7 +1885,7 @@ dependencies = [
[[package]]
name = "zcash_address"
version = "0.0.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=0ec7f97c976d55e1a194a37b27f247e8887fca1d#0ec7f97c976d55e1a194a37b27f247e8887fca1d"
source = "git+https://github.com/zcash/librustzcash.git?rev=69c3b4b5e143bb349cca76c677b7cd0a8556b28f#69c3b4b5e143bb349cca76c677b7cd0a8556b28f"
dependencies = [
"bech32",
"blake2b_simd",
@ -1897,7 +1897,7 @@ dependencies = [
[[package]]
name = "zcash_encoding"
version = "0.0.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=0ec7f97c976d55e1a194a37b27f247e8887fca1d#0ec7f97c976d55e1a194a37b27f247e8887fca1d"
source = "git+https://github.com/zcash/librustzcash.git?rev=69c3b4b5e143bb349cca76c677b7cd0a8556b28f#69c3b4b5e143bb349cca76c677b7cd0a8556b28f"
dependencies = [
"byteorder",
"nonempty",
@ -1906,7 +1906,7 @@ dependencies = [
[[package]]
name = "zcash_history"
version = "0.2.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=0ec7f97c976d55e1a194a37b27f247e8887fca1d#0ec7f97c976d55e1a194a37b27f247e8887fca1d"
source = "git+https://github.com/zcash/librustzcash.git?rev=69c3b4b5e143bb349cca76c677b7cd0a8556b28f#69c3b4b5e143bb349cca76c677b7cd0a8556b28f"
dependencies = [
"bigint",
"blake2b_simd",
@ -1916,7 +1916,7 @@ dependencies = [
[[package]]
name = "zcash_note_encryption"
version = "0.0.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=0ec7f97c976d55e1a194a37b27f247e8887fca1d#0ec7f97c976d55e1a194a37b27f247e8887fca1d"
source = "git+https://github.com/zcash/librustzcash.git?rev=69c3b4b5e143bb349cca76c677b7cd0a8556b28f#69c3b4b5e143bb349cca76c677b7cd0a8556b28f"
dependencies = [
"blake2b_simd",
"byteorder",
@ -1931,7 +1931,7 @@ dependencies = [
[[package]]
name = "zcash_primitives"
version = "0.5.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=0ec7f97c976d55e1a194a37b27f247e8887fca1d#0ec7f97c976d55e1a194a37b27f247e8887fca1d"
source = "git+https://github.com/zcash/librustzcash.git?rev=69c3b4b5e143bb349cca76c677b7cd0a8556b28f#69c3b4b5e143bb349cca76c677b7cd0a8556b28f"
dependencies = [
"aes",
"bip0039",
@ -1965,7 +1965,7 @@ dependencies = [
[[package]]
name = "zcash_proofs"
version = "0.5.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=0ec7f97c976d55e1a194a37b27f247e8887fca1d#0ec7f97c976d55e1a194a37b27f247e8887fca1d"
source = "git+https://github.com/zcash/librustzcash.git?rev=69c3b4b5e143bb349cca76c677b7cd0a8556b28f#69c3b4b5e143bb349cca76c677b7cd0a8556b28f"
dependencies = [
"bellman",
"blake2b_simd",

View File

@ -73,8 +73,8 @@ codegen-units = 1
ed25519-zebra = { git = "https://github.com/ZcashFoundation/ed25519-zebra.git", rev = "d3512400227a362d08367088ffaa9bd4142a69c7" }
incrementalmerkletree = { git = "https://github.com/zcash/incrementalmerkletree", rev = "b7bd6246122a6e9ace8edb51553fbf5228906cbb" }
orchard = { git = "https://github.com/zcash/orchard.git", rev = "68b790c7dadade049f44ad4aafa0ff71a3a10e91" }
zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "0ec7f97c976d55e1a194a37b27f247e8887fca1d" }
zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "0ec7f97c976d55e1a194a37b27f247e8887fca1d" }
zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "0ec7f97c976d55e1a194a37b27f247e8887fca1d" }
zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "0ec7f97c976d55e1a194a37b27f247e8887fca1d" }
zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "0ec7f97c976d55e1a194a37b27f247e8887fca1d" }
zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "69c3b4b5e143bb349cca76c677b7cd0a8556b28f" }
zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "69c3b4b5e143bb349cca76c677b7cd0a8556b28f" }
zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "69c3b4b5e143bb349cca76c677b7cd0a8556b28f" }
zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "69c3b4b5e143bb349cca76c677b7cd0a8556b28f" }
zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "69c3b4b5e143bb349cca76c677b7cd0a8556b28f" }

View File

@ -44,6 +44,7 @@ JSON_TEST_FILES = \
test/data/merkle_commitments_sapling.json \
test/data/sapling_key_components.json \
test/data/unified_addrs.json \
test/data/unified_full_viewing_keys.json \
test/data/zip0244.json
RAW_TEST_FILES = test/data/alertTests.raw

View File

@ -10,6 +10,7 @@
#include "json_test_vectors.h"
#include "test/data/unified_addrs.json.h"
#include "test/data/unified_full_viewing_keys.json.h"
TEST(Keys, EncodeAndDecodeSapling)
{
@ -102,7 +103,7 @@ namespace libzcash {
}
}
TEST(Keys, EncodeAndDecodeUnified)
TEST(Keys, EncodeAndDecodeUnifiedAddresses)
{
SelectParams(CBaseChainParams::MAIN);
KeyIO keyIO(Params());
@ -131,8 +132,7 @@ TEST(Keys, EncodeAndDecodeUnified)
if (!test[2].isNull()) {
auto data = ParseHex(test[2].get_str());
CDataStream ss(
reinterpret_cast<const char*>(data.data()),
reinterpret_cast<const char*>(data.data() + data.size()),
data,
SER_NETWORK,
PROTOCOL_VERSION);
libzcash::SaplingPaymentAddress r;
@ -164,3 +164,60 @@ TEST(Keys, EncodeAndDecodeUnified)
}
}
}
TEST(Keys, EncodeAndDecodeUnifiedFullViewingKeys)
{
SelectParams(CBaseChainParams::MAIN);
KeyIO keyIO(Params());
UniValue ua_tests = read_json(MAKE_STRING(json_tests::unified_full_viewing_keys));
for (size_t idx = 0; idx < ua_tests.size(); idx++) {
UniValue test = ua_tests[idx];
std::string strTest = test.write();
if (test.size() < 1) // Allow for extra stuff (useful for comments)
{
FAIL() << "Bad test: " << strTest;
continue;
}
if (test.size() == 1) continue; // comment
libzcash::UnifiedFullViewingKeyBuilder builder;
// ["t_key_bytes, sapling_fvk_bytes, orchard_fvk_bytes, unknown_fvk_typecode, unknown_fvk_bytes, unified_fvk"]
// These were added to the UA in preference order by the Python test vectors.
if (!test[0].isNull()) {
auto data = ParseHex(test[0].get_str());
ASSERT_EQ(data.size(), 65);
CDataStream ss(data, SER_NETWORK, PROTOCOL_VERSION);
auto decoded = CChainablePubKey::Read(ss);
ASSERT_TRUE(builder.AddTransparentKey(decoded));
}
if (!test[1].isNull()) {
auto data = ParseHex(test[1].get_str());
ASSERT_EQ(data.size(), 128);
CDataStream ss(data, SER_NETWORK, PROTOCOL_VERSION);
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;
auto built = builder.build();
ASSERT_TRUE(built.has_value());
auto keystrBytes = ParseHex(test[5].get_str());
std::string keystr(keystrBytes.begin(), keystrBytes.end());
auto decoded = libzcash::UnifiedFullViewingKey::Decode(keystr, Params());
ASSERT_TRUE(decoded.has_value());
EXPECT_EQ(decoded.value().GetTransparentKey(), built.value().GetTransparentKey());
EXPECT_EQ(decoded.value().GetSaplingKey(), built.value().GetSaplingKey());
}
}

View File

@ -5,6 +5,8 @@
#ifndef ZCASH_KEY_CONSTANTS_H
#define ZCASH_KEY_CONSTANTS_H
#include <string>
class KeyConstants
{
public:

View File

@ -190,6 +190,10 @@ public:
return ret;
}
std::string operator()(const libzcash::UnifiedFullViewingKey& ufvk) const {
return ufvk.Encode(keyConstants);
}
std::string operator()(const libzcash::InvalidEncoding& no) const { return {}; }
};
@ -485,6 +489,13 @@ std::string KeyIO::EncodeViewingKey(const libzcash::ViewingKey& vk)
libzcash::ViewingKey KeyIO::DecodeViewingKey(const std::string& str)
{
// Try parsing as a Unified full viewing key
auto ufvk = libzcash::UnifiedFullViewingKey::Decode(str, keyConstants);
if (ufvk.has_value()) {
return ufvk.value();
}
// Fall back on trying Sprout or Sapling.
return DecodeAny<libzcash::ViewingKey,
libzcash::SproutViewingKey,
libzcash::SaplingExtendedFullViewingKey>(

View File

@ -134,6 +134,14 @@ bool CExtPubKey::Derive(CExtPubKey &out, unsigned int _nChild) const {
return (!secp256k1_ecdsa_signature_normalize(secp256k1_context_verify, NULL, &sig));
}
/* static */ std::optional<CChainablePubKey> CChainablePubKey::FromParts(ChainCode chaincode, CPubKey pubkey) {
if (pubkey.IsCompressed()) {
return CChainablePubKey(chaincode, pubkey);
} else {
return std::nullopt;
}
}
/* static */ int ECCVerifyHandle::refcount = 0;
ECCVerifyHandle::ECCVerifyHandle()

View File

@ -203,6 +203,55 @@ public:
bool Derive(CPubKey& pubkeyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc) const;
};
class CChainablePubKey {
private:
ChainCode chaincode;
CPubKey pubkey;
CChainablePubKey() {}
CChainablePubKey(ChainCode chaincode, CPubKey pubkey): chaincode(chaincode), pubkey(pubkey) {}
public:
static std::optional<CChainablePubKey> FromParts(ChainCode chaincode, CPubKey pubkey);
const ChainCode& GetChainCode() const {
return chaincode;
}
const CPubKey& GetPubKey() const {
return pubkey;
}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(chaincode);
if (ser_action.ForRead()) {
std::array<uint8_t, CPubKey::COMPRESSED_PUBLIC_KEY_SIZE> pubkeyBytes;
READWRITE(pubkeyBytes);
pubkey = CPubKey(pubkeyBytes.begin(), pubkeyBytes.end());
assert(pubkey.IsCompressed());
} else {
assert(pubkey.size() == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE);
std::array<uint8_t, CPubKey::COMPRESSED_PUBLIC_KEY_SIZE> pubkeyBytes;
std::copy(pubkey.begin(), pubkey.end(), pubkeyBytes.begin());
READWRITE(pubkeyBytes);
}
}
template <typename Stream>
static CChainablePubKey Read(Stream& stream) {
CChainablePubKey key;
stream >> key;
return key;
}
friend bool operator==(const CChainablePubKey &a, const CChainablePubKey &b)
{
return a.chaincode == b.chaincode && a.pubkey == b.pubkey;
}
};
struct CExtPubKey {
unsigned char nDepth;
unsigned char vchFingerprint[4];

View File

@ -307,39 +307,47 @@ extern "C" {
);
/**
* Derive a PaymentAddress from an ExtendedFullViewingKey. Returns 'false'
* if no valid address can be derived for the specified diversifier index.
* Derive a PaymentAddress from a (SaplingFullViewingKey, DiversifierKey)
* pair. Returns 'false' if no valid address can be derived for the
* specified diversifier index.
*
* Arguments:
* - xfvk: [c_uchar; 169] the serialized form of a Sapling extended full viewing key
* - j: [c_uchar; 11] the 88-bit diversifier address at which to start searching,
* encoded in little-endian order
* - addr_ret: [c_uchar; 43] array to which the returned address will be written,
* if the specified diversifier index `j` produces a valid address.
* - fvk: [c_uchar; 96] the serialized form of a Sapling full viewing key
* - dk: [c_uchar; 32] the byte representation of a Sapling diversifier key
* - j: [c_uchar; 11] the 88-bit diversifier index, encoded in little-endian
* order
* - addr_ret: [c_uchar; 43] array to which the returned address will be
* written, if the specified diversifier index `j` produces a valid
* address.
*/
bool librustzcash_zip32_xfvk_address(
const unsigned char *xfvk,
bool librustzcash_zip32_sapling_address(
const unsigned char *fvk,
const unsigned char *dk,
const unsigned char *j,
unsigned char *addr_ret
);
/**
* Derive a PaymentAddress from an ExtendedFullViewingKey by searching the
* space of valid diversifiers starting at diversifier index `j`. This will
* always return a valid address along with the diversifier index that produced
* the address unless no addresses can be derived at any diversifier index >= `j`,
* in which case this function will return `false`.
* Derive a PaymentAddress from a (SaplingFullViewingKey, DiversifierKey)
* pair by searching the space of valid diversifiers starting at
* diversifier index `j`. This will always return a valid address along
* with the diversifier index that produced the address unless no addresses
* can be derived at any diversifier index >= `j`, in which case this
* function will return `false`.
*
* Arguments:
* - xfvk: [c_uchar; 169] the serialized form of a Sapling extended full viewing key
* - j: [c_uchar; 11] the 88-bit diversifier address at which to start searching,
* encoded in little-endian order
* - j_ret: [c_uchar; 11] array that will store the diversifier index at which the
* returned address was found
* - addr_ret: [c_uchar; 43] array to which the returned address will be written
* - fvk: [c_uchar; 96] the serialized form of a Sapling full viewing key
* - dk: [c_uchar; 32] the byte representation of a Sapling diversifier key
* - j: [c_uchar; 11] the 88-bit diversifier index at which to start
* searching, encoded in little-endian order
* - j_ret: [c_uchar; 11] array that will store the diversifier index at
* which the returned address was found
* - addr_ret: [c_uchar; 43] array to which the returned address will be
* written
*/
bool librustzcash_zip32_find_xfvk_address(
const unsigned char *xfvk,
bool librustzcash_zip32_find_sapling_address(
const unsigned char *fvk,
const unsigned char *dk,
const unsigned char *j,
unsigned char *j_ret,
unsigned char *addr_ret

View File

@ -0,0 +1,111 @@
// Copyright (c) 2021 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
#ifndef ZCASH_RUST_INCLUDE_RUST_UNIFIED_KEYS_H
#define ZCASH_RUST_INCLUDE_RUST_UNIFIED_KEYS_H
#ifdef __cplusplus
extern "C" {
#endif
//
// Unified full viewing keys
//
/**
* Void pointer type representing a reference to a Rust-allocated
* unified::Ufvk value
*/
struct UnifiedFullViewingKeyPtr;
typedef struct UnifiedFullViewingKeyPtr UnifiedFullViewingKeyPtr;
/**
* Free the memory allocated for the given address::unified::Ufvk;
*/
void unified_full_viewing_key_free(UnifiedFullViewingKeyPtr* ptr);
/**
* Clones the given Unified full viewing key and returns
* a pointer to the newly created value. Both the original
* one's memory and the newly allocated one need to be freed
* independently.
*/
UnifiedFullViewingKeyPtr* unified_full_viewing_key_clone(
const UnifiedFullViewingKeyPtr* ptr);
/**
* Parses a unified full viewing key from the given string.
*
* - If the key does not parse correctly, or the network for which the
* key was encoded does not match the specified network the returned
* pointer will be null.
*/
UnifiedFullViewingKeyPtr* unified_full_viewing_key_parse(
const char* network,
const char* str);
/**
* Serializes a unified full viewing key and returns its string representation.
*
* The returned string's memory must be freed by the caller using
* `zcash_address_string_free`.
*/
char* unified_full_viewing_key_serialize(
const char* network,
const UnifiedFullViewingKeyPtr* full_viewing_key);
/**
* Reads the transparent component of a unified full viewing key.
*
* `tkeyout` must be of length 65.
*
* Returns `true` if the UFVK contained a transparent component, `false`
* otherwise. If this returns `true`, the transparent key will be copied to
* `tkeyout` as the byte representation of the `(ChainCode, CPubKey)` pair.
* If it returns `false` then `tkeyout` will be unchanged.
*/
bool unified_full_viewing_key_read_transparent(
const UnifiedFullViewingKeyPtr* full_viewing_key,
unsigned char* tkeyout);
/**
* Reads the Sapling component of a unified full viewing key.
*
* `skeyout` must be of length 128.
*
* Returns `true` if the UFVK contained a Sapling component,
* `false` otherwise. The bytes of the `(ak, nk, ovk, dk)` fields
* of the viewing key, in the encoding given by `EncodeExtFVKParts`
* defined in ZIP 32, will be copied to `skeyout` if `true` is returned.
* If `false` is returned then `skeyout` will be unchanged.
*/
bool unified_full_viewing_key_read_sapling(
const UnifiedFullViewingKeyPtr* full_viewing_key,
unsigned char* skeyout);
/**
* Constructs a unified full viewing key from the binary encodings
* of its constituent parts.
*
* If `t_key` is not `null`, it must be of length 65 and must be the concatenated
* bytes of the serialized `(ChainCode, CPubKey)` pair.
*
* If `sapling_key` is not `null`, it must be of length 128 and must be the concatenated
* bytes of the `(ak, nk, ovk, dk)` fields in the encoding given by
* `EncodeExtFVKParts` defined in ZIP 32.
*
* Returns a pointer to newly allocated UFVK if the operation succeeds,
* or the null pointer otherwise. The pointer returned by this function
* must be freed by the caller with `unified_full_viewing_key_free`.
*/
UnifiedFullViewingKeyPtr* unified_full_viewing_key_from_components(
const unsigned char* t_key,
const unsigned char* sapling_key);
#ifdef __cplusplus
}
#endif
#endif // ZCASH_RUST_INCLUDE_RUST_UNIFIED_KEYS_H

View File

@ -5,7 +5,10 @@ use std::{
};
use libc::{c_char, c_void};
use zcash_address::{unified, FromAddress, Network, ToAddress, ZcashAddress};
use zcash_address::{
unified::{self, Container, Encoding},
FromAddress, Network, ToAddress, ZcashAddress,
};
use zcash_primitives::sapling;
pub type UnifiedAddressObj = NonNull<c_void>;
@ -23,7 +26,7 @@ pub type GetReceiverLenCb =
pub type GetReceiverDataCb =
unsafe extern "C" fn(ua: Option<UnifiedAddressObj>, index: usize, data: *mut u8, length: usize);
fn network_from_cstr(network: *const c_char) -> Option<Network> {
pub(crate) fn network_from_cstr(network: *const c_char) -> Option<Network> {
match unsafe { CStr::from_ptr(network) }.to_str().unwrap() {
"main" => Some(Network::Main),
"test" => Some(Network::Test),
@ -69,7 +72,7 @@ impl UnifiedAddressHelper {
}
self.ua
.receivers()
.items()
.into_iter()
.map(|receiver| match receiver {
unified::Receiver::Orchard(data) => {
@ -209,7 +212,7 @@ pub extern "C" fn zcash_address_serialize_unified(
}
};
let ua: unified::Address = match receivers.try_into() {
let ua = match unified::Address::try_from_items_preserving_order(receivers) {
Ok(ua) => ua,
Err(e) => {
tracing::error!("{}", e);

View File

@ -46,14 +46,15 @@ use zcash_primitives::{
block::equihash,
constants::{CRH_IVK_PERSONALIZATION, PROOF_GENERATION_KEY_GENERATOR, SPENDING_KEY_GENERATOR},
merkle_tree::MerklePath,
sapling::{merkle_hash, spend_sig},
sapling::{
keys::FullViewingKey,
note_encryption::sapling_ka_agree,
redjubjub::{self, Signature},
Diversifier, Note, PaymentAddress, ProofGenerationKey, Rseed, ViewingKey,
},
sapling::{merkle_hash, spend_sig},
transaction::components::Amount,
zip32,
zip32::{self, sapling_address, sapling_find_address},
};
use zcash_proofs::{
circuit::sapling::TREE_DEPTH as SAPLING_TREE_DEPTH,
@ -74,6 +75,7 @@ mod incremental_merkle_tree_ffi;
mod orchard_ffi;
mod orchard_keys_ffi;
mod transaction_ffi;
mod unified_keys_ffi;
mod zip339_ffi;
mod test_harness_ffi;
@ -1080,16 +1082,17 @@ pub extern "C" fn librustzcash_zip32_xfvk_derive(
/// Derive a PaymentAddress from an ExtendedFullViewingKey.
#[no_mangle]
pub extern "C" fn librustzcash_zip32_xfvk_address(
xfvk: *const [c_uchar; 169],
pub extern "C" fn librustzcash_zip32_sapling_address(
fvk: *const [c_uchar; 96],
dk: *const [c_uchar; 32],
j: *const [c_uchar; 11],
addr_ret: *mut [c_uchar; 43],
) -> bool {
let xfvk = zip32::ExtendedFullViewingKey::read(&unsafe { *xfvk }[..])
.expect("valid ExtendedFullViewingKey");
let fvk = FullViewingKey::read(&unsafe { *fvk }[..]).expect("valid Sapling FullViewingKey");
let dk = zip32::DiversifierKey(unsafe { *dk });
let j = zip32::DiversifierIndex(unsafe { *j });
match xfvk.address(j) {
match sapling_address(&fvk, &dk, j) {
Some(addr) => {
let addr_ret = unsafe { &mut *addr_ret };
addr_ret.copy_from_slice(&addr.to_bytes());
@ -1102,17 +1105,18 @@ pub extern "C" fn librustzcash_zip32_xfvk_address(
/// Derive a PaymentAddress from an ExtendedFullViewingKey.
#[no_mangle]
pub extern "C" fn librustzcash_zip32_find_xfvk_address(
xfvk: *const [c_uchar; 169],
pub extern "C" fn librustzcash_zip32_find_sapling_address(
fvk: *const [c_uchar; 96],
dk: *const [c_uchar; 32],
j: *const [c_uchar; 11],
j_ret: *mut [c_uchar; 11],
addr_ret: *mut [c_uchar; 43],
) -> bool {
let xfvk = zip32::ExtendedFullViewingKey::read(&unsafe { *xfvk }[..])
.expect("valid ExtendedFullViewingKey");
let fvk = FullViewingKey::read(&unsafe { *fvk }[..]).expect("valid Sapling FullViewingKey");
let dk = zip32::DiversifierKey(unsafe { *dk });
let j = zip32::DiversifierIndex(unsafe { *j });
match xfvk.find_address(j) {
match sapling_find_address(&fvk, &dk, j) {
Some((j, addr)) => {
let j_ret = unsafe { &mut *j_ret };
let addr_ret = unsafe { &mut *addr_ret };

View File

@ -0,0 +1,131 @@
use libc::c_char;
use std::ffi::{CStr, CString};
use tracing::error;
use zcash_address::unified::{Container, Encoding, Fvk, Ufvk};
use crate::address_ffi::network_from_cstr;
#[no_mangle]
pub extern "C" fn unified_full_viewing_key_free(key: *mut Ufvk) {
if !key.is_null() {
drop(unsafe { Box::from_raw(key) });
}
}
#[no_mangle]
pub extern "C" fn unified_full_viewing_key_clone(key: *const Ufvk) -> *mut Ufvk {
unsafe { key.as_ref() }
.map(|key| Box::into_raw(Box::new(key.clone())))
.unwrap_or(std::ptr::null_mut())
}
#[no_mangle]
pub extern "C" fn unified_full_viewing_key_parse(
network: *const c_char,
encoded: *const c_char,
) -> *mut Ufvk {
let network = match network_from_cstr(network) {
Some(n) => n,
None => {
return std::ptr::null_mut();
}
};
match unsafe { CStr::from_ptr(encoded) }.to_str() {
Ok(encoded) => match Ufvk::decode(encoded) {
Ok((parsed_network, fvk)) if parsed_network == network => Box::into_raw(Box::new(fvk)),
Ok((parsed_network, _)) => {
error!(
"Key was encoded for a different network ({:?}) than what was requested ({:?})",
parsed_network, network,
);
std::ptr::null_mut()
}
Err(e) => {
error!("Failure decoding unified full viewing key: {}", e);
std::ptr::null_mut()
}
},
Err(e) => {
error!("Failure reading bytes of unified full viewing key: {}", e);
std::ptr::null_mut()
}
}
}
#[no_mangle]
pub extern "C" fn unified_full_viewing_key_serialize(
network: *const c_char,
key: *const Ufvk,
) -> *mut c_char {
let key = unsafe { key.as_ref() }.expect("Unified full viewing key pointer may not be null.");
match network_from_cstr(network) {
Some(n) => CString::new(key.encode(&n)).unwrap().into_raw(),
None => std::ptr::null_mut(),
}
}
#[no_mangle]
pub extern "C" fn unified_full_viewing_key_read_transparent(
key: *const Ufvk,
out: *mut [u8; 65],
) -> 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::P2pkh(data) = r {
*out = *data;
return true;
}
}
false
}
#[no_mangle]
pub extern "C" fn unified_full_viewing_key_read_sapling(
key: *const Ufvk,
out: *mut [u8; 128],
) -> 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::Sapling(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],
) -> *mut Ufvk {
let t_key = unsafe { t_key.as_ref() };
let sapling_key = unsafe { sapling_key.as_ref() };
let mut items = vec![];
if let Some(t_bytes) = t_key {
items.push(Fvk::P2pkh(*t_bytes));
}
if let Some(sapling_bytes) = sapling_key {
items.push(Fvk::Sapling(*sapling_bytes));
}
match Ufvk::try_from_items(items) {
Ok(ufvk) => Box::into_raw(Box::new(ufvk)),
Err(e) => {
error!(
"An error occurred constructing the unified full viewing key: {:?}",
e
);
std::ptr::null_mut()
}
}
}

View File

@ -0,0 +1,14 @@
[
["From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/unified_full_viewing_keys.py"],
["t_key_bytes, sapling_fvk_bytes, orchard_fvk_bytes, unknown_fvk_typecode, unknown_fvk_bytes, unified_fvk"],
[null, "cfb835e7c05c80c2a15a58702bc529a44e1a815ef79124f23709214cf0167ac4e6340b493dca8e4bee114259dc35edc4c296ffd53869885531d1bdb27008bbcd6fec092ad5c4d1f68819f41ae447db96df4a5f110018f47060916ec54884f1cc27a0d4c0bca90984cdf39fb4cc61ceee78ddaa2a45af871f49f04e98b02fb16b", null, 65535, null, "757669657731747878783339707833736a676478796c6d6636666876706e6878667966717376756e3863737330723678717830726b3974767a3076727a74756a74683474716e7534367877657035367279396a643537687972726c36757467657a356a717232716466737a79787265686b64686774757964376d78756e6e6133327732356d396b7771387163687377673476686632796d736b376c6e7637786a3864356b347a7272343370756e746b6d666b396e346a636b66763237677063376e747765726c6c756439307a346c356c71786a68366333356b376135786c6d663563726467677537366c307572657475787333386839"],
[null, "04da0d94cb0a6397067a81a88ef422e56678e0ba232eb4dd6b05b98bc5e3461cd4a52b366a7df1f3a871854bfe1492711dc5130b35441748caa2742959279ce31e2b3604995d4ccedc4618ad16cdd2c0d42a6d36fb3a0610054cedef30beca20d187f32ce02f8ca357b575e705cda7ef8e1c68b9110381bd3958e0659a084205", "9bf5e479a60cfa3893a2a7693488b3fa016402851263e63c48540b3daff13814cd71a2ab1f78c561c768a3641dcf7a7d6b473393a629841c81f93afa87689035a119c9f7dac6fb9e7cf8fd6aa40f5f7f9e75b161d5f7269d28a4211ab84a8408", 65532, null, "75766965773135797678377477766a376a726a67766d6e6c797474683632396e7861746c7837686e79727a707a3468337a74776a79786c7733793770657a737671397276643470726779326474726a6c736d6c326c76366c6a7575306a7274766b6d6d366c6a32793768706c70686b383978656b66636565687a637a6777773237756538763272376e3077727339636b78376134756c6365766b777830363077667139613379357932746d337132777163756c796d3232396668387474386a6d376c683863367639707a67613274396e6c78673630377478353673303775757177337673336d78663676716b68353071636a33677678347664367a367379356e3039387565636c396a707968737178333934677337323539786b33796c376764326e333370767371707979377064393734347271786e73343073327a323963796b3435766a763765756b713976777a3879306163383539707a7166386b6d6c6b767678723930716b326e30376c6e7264393061363467376b6472336366356a637665726c7173667134366e6e71777137386b68"],
["546c1fe01f7e9c8e36d6a5e29d4e30a73594bf5098421c69378af1e40f64e12503f4e4887b87079d7b7647f01a93a73c7a7d517840575c3246710adbdd752ed2d6", "fc02759ff2853b5e9e15842bef22c76023d43db7c265f120ed175713195f1240f63aee02ec23f146e9e25d25605fbae5472cdedc3b4c31c66b76fe9e6e47eeca79d5fb6a84d152820daaf89e99551d068d99cdf9be065007bc25f245ea62631d17b3bc77f62f35bd4205e6f682b1f9e824ecea53e271b80ff6bc79ef68a20ab5", "c36559ed79d1fecbe1de93d57698e915802f0ccd097adb4e448caef8c48d6c1e439482a1e5636131cba024f891b3bb1dfe333773415eeecf7a42e37dbd934e3a97d92dc90afa0ec3e96eeed9238dfcb6da23799cff14d477368d9ddc330afe39", 65532, null, "757669657731796e727935646c356c676b6733656a61326b386d777779646661357a363465726674657936383365776561326e3068333436636134326e6d7561347170636b63787565707073366e326d656a6c6b64363930333772766d7a61343677306c6d3035676a7070686566756a7a6d6734663876366d7130746b676766376667727138307a6d6d6b756467737739397465687363396d6a65773966367238366b6463646a7a666e66773065326b6d713567646b6866617175797167676b64616d68727a726e6a756663777739386e6577647a7479726d353775757037673338383730703932763776787a727377336c6366797a37616e326473616637757733663672716b32747a6a396c66356371376378346a77767667776a3078787064723433786a6d63646b366b6c6668773379663672617168383676676c61706a636d6b6879776a67366b687161777561667077716a6b776a75756c6a646e756b68646c6b6e726c3878307674727075727335636b7a357734683030397674657165646478786e63666e736c6e6830687074723665766566366e7a393268723838766c616a79366b6378366d306a67613267706b7773736b347277773373766d6e61683437656b64687430713964647467666e7a6736736d6b656c66336a723834707473706c376e37646575397a306d65616b756b336b3867707768703532"],
[null, null, "13a3e9649004641d3fc4da1cdefc4c5c6c457b0aee6e668d1d57a58bc48b0e1501a9e2572d16a3cb72c7b539582a46d8f67823da02235d41d49c12788bceeb334500fef8adfc1bcbaa0eede5b48ea7d3e9ec2a774986aac43050c50aa24f1d0f", 65533, null, "757669657731686d3672366e6434747463707137786e326a307367796a336d63726e6633336b796b3574797271666c70663774327266336a6c6530713839616a6870327764746164397533646e76356b6c746171767576647a6c7770397679726378666535676b763078646a6578326e68707839336e65367876307770386130676c6e36746a6b76796b7174686166726e686e6b6633676138656a766676717636386735633974386a75656e7a6833396766776a64637238786d7476677937656d7465"],
[null, null, "33ca395730a107b148032a999349f6dc9983daab9d01ffb1180b56ba7038221d1bf174c534ae1adb7a61a30ad0e3b36c52cf90957491ed84f90b5bd42c76833ef0cf897fe579461bc8db0b37e2da35766f2ee00712416c87f0a679fc55d4791f", 65531, null, "7576696577316478386a3774756b7473686678737930336167706a68633878326d716137343338353861343274736c327335786b78647a706534617664677363647978366163676c35736c656a3978616e33307479616735707333706a6a7375677075307276343237656b6c336c78616c6738637378726568667670717635306672617772687868716b38766a387975353270336875333872786c3772726e6c766a663374776b7467336b67323667656765636d30786873726d673671667036327563"],
["9db6990ed83dd64af3597c04323ea51b0052ad8084a8b9da948d320dadd64f540215761b6d5155ba66df44f224e914e4a8bb6f2fee6820a865ac9fbfb327c11507", null, "a71cccc7d3f4fd389ba45bc4dc4e9b3d91d1fdc70eb15ab92aced578409e272f15181f6ae4b8412caf1044dab7749111cdba8c43a4ffad1de39bb22d2dd150077e9343b886179115512836623a664e3125d2faf44a1308e3dbd525da73e46523", 65534, null, "75766965773166326d6d6a783867786e376a716a796a397872776c6b32656d61617770393561326e716a3366373574766b37737a766d6430686e6d7a6d79637732336773343870646a37676d326c36637573763464733935666d656d723972723966787838333370776a63336c7a353330376c787a7632366630757132756b70776167717730786c7a6a746838386476776536616a3339686d6e7661307561716b6a656c73373268386563757539753273636c30756c373239687a397437793537656b617978777375326638366164306c3536727237653374353378736333787333347075766167667564787734766a377039336c346377303232747238706b746567716d386e32737835687061747a3776357130636d3865767032356d77776b6767657568677366737a357078"],
["1ebd931de518856878f73476f21a482ec9378365c8f7393c94e2885315eb4671021c3e1aa400191c4609ba93df32612f46dfa68aebcfb80778b7fb59bb563e0a7a", null, "3d301cbf5f38ad466992b6466c3377d9f314e6e0e456cd8879f9f9a2db838232c3c801b93141a1ddc29f1079a9b3b46126ed23aeabc4007912798cadf9cfc00e800540cf11186e253036ae5a53f875c50a4a7940021523a92d8b359c140acb31", 65534, null, "7576696577317237386b6434393237373270396371646165336a396a6a647572637a76357a75633632706b366b3371756674776339396735666c7a6366356470737a676b6d6574307a6533726d38746c33356d63647a686564616a74386e7a397877377676656c673275397137756a373876736372666d383466703937633770753867326e78376a3476346c3966633530637671756b396d37673536306d386e3468707972726e6c6a39726b377537726d657578303265723876727a727167326136767535766b786d6b66753368326834666b30777a336c337735657173676c393374777037616d6d3936726b6b30356d6e326b7970677a3863636e6c3778786e3973616b35787a6c65617072736c3630653573323870346e796875356b6a32776b35726c6676766e3434396d35"],
["26c1039586a7afcf4a0d9c731e985d99589c8bb838e8aaf745533ed9e8ae3a1c02ca6a53e13c134c1fb70013b1c3c7f59788b28df32fac5c220f565bd926dca633", null, "ffdffc7112cf550135fa5476272b24289fcabc5401b16db739d1df8f4493f1143c3cc90b837152144de475182209b0c169559f4a91fb788403b3b7642cc99c0d519abc658bfae98e12c9878d9e16c22937cc8182ebac54f15e307a2e639da239", 65535, null, "75766965773139706d666b617678647639777a6867766670653070753678727366373967616e613832396c3277667537713833336e63667a71657732656c637579643674757139683770656472397871763068713461637768396e647332776436677474357633776530777964336374736463336866676b7968637779776d753733323076666b30636b6e70326e65706a64686d756b3375746365613267796d78306630327a6d6c61386a706137656c6d7768796b7434637371333739717a356133386579346b79353077373064353330797a6a6d686c7473637a32673832766a7479616b65737366323776397177736b79713734786c6a63333530663767793036306a686b6c673871727671746535656c6d637374336e6b30766b6d6c6e6d6d7437367337657363656a707263"],
[null, "deb352b3dfeb09d42a96e77ce38e8a6bd00a1052e565215e1325e0712703368c8dbc0cd5eb82701bbd5903e60d488ef8f8944940ca7d87efbbd6c1eacf9ede8019a3b8fde82c151e11321e54a49aaf6de8b9d6515816a5104fb6ea54ed33873dc4a6ba5a80c1ee0f78378db0052388372aecdde7f53fb35320bec33763ddaa51", "37e5d0367502f509f94f077449cbd07473b9d2b630a5b4f8cefa689543d27028b01e4b736cc5620dc82093b4077b12fab582f225f3a5e58a76921cdad17dd70d458aee1ef78af37e9aadfffbd95db1046ad5b9f29ea4297050cadf9825721632", 65533, "857deecc40a98d5f2935395ee4762dd21afdbb5d47fa9a6dd984d567db2857b927b7fae2db587105415d4642789d38f50b8dbcc129cab3d17d19f3355bcf73cecb8cb8a5da01307152f13936a270572670dc82d39026c6cb4cd4b0f7f5aa2a4f5a", "757669657731756d6b78787175716e7377307a73717a6175327235376d3863706a75363576787865636d6437686c726d677367326c63653932396c6a3563776c636e65707973636577306371353577646d657072767476337a707665356465727976716d726c7035796770346c733264716d796764636738636d6132763878656e6e7a637575326132736c6c7938646c646479757a717573677064736e7a61717933747332736c70746b6d386a737567766a333938766e336b7472643975757167336b3376737a33687a7a6361736c3765646e676868366c78707477746e6468377961616665796c6372353870686b66636635307a6b64666d6b36736133366e36646a687536636e68347278663530766b79396a767663663075707a346479713876367465687732673661616b737730747873336530646b3973763336613371797874356d68636373366768387265336736757633327a3370737879767133617930776e7332616b756666376a333833677130757036706d6e3467366c763937666a646d73397a336735777261383338786668657a776361363574796174333078637933326a76357279356e37613363306d3036713763616e71736e72366133673330716b387265616b717a716d64366b656338667663336d7a6a6e6d39653564376564686a6c36357a67636c743272756e6a34337a6d6d73327665636b7176366e3338716477303863736d7378746b643068796666366a6e3266377732376434726a7168326c7a637638386d30383763393839"],
["d715406f2fdd2afa733f5f641c8c21862a1bafce2609d9eecfa158cfb5cd79f803ff3f19851764670df1660c9cb56918abc2a0691eb8b89e705eb8e65b0f4e0dcb", "068d407022db8e5dd0730882bd54851ca0797a00dff60e358d28a04df20793838ba1618a6871da256cdbf7d8ad2ce81d9ce01df64765bc7c14ac74e7ed60129090fcb061b3e672a742aa0f5db728265947355b6375f3d2226b25129dced2e0991d02f739d2d822df5d41edc122b2330916ba36ca09e80cf07f99be4a45fce8e7", "d0e5967a78a4ba305dc8c47ad6ea668d2e88e11575d2ec1f082c740b5fdc050ddb57fa080007c5d868dd8670904b76a82959e8482795ab3ba4cbdc7bb37b1822d2f3ccda83c005fe5c571dbec5074f159f345ad2f60edf8364e4ddf983f91235", 65533, "ad03bc0cda4a44946c00e1b1a1df0e5b87b5bece477a709649e950060591394812951e1fe3895b8cc3d14d2cf6556df6ed4b4ddd3d9a69f53357d7767f4f5ccbdbc596631277f8fecd08cb056b95e3025b9792fff7f244fc716269b926d62e95", "757669657731666b6e393734747468686363306b71796d726a7467637370646c7170613768346e356e7439386b76773761326a676a686174706a713478656a76673879646e747074706e327a6c64707471386371716e646a6c67336c6b786b66776a7635726e39767638636a3666783567307a387a66746e6470726b7073387937656d34737a79327679386a70336832653678386b64717170656e356865387272326466797072767763713563756e7639396c67306a6b397233796c777064617a723779757a357938756a773071366e333939756d643236366a37396a7371776d6c7967767533776e666b797a3335647964687861706c63327578713572613237396e77396e73776379326837676873673930376d70733279686578666876323576776c343461383833376a63777336766365776a7434347a357333336e71756c396d35737a6d6e6179346476763364777568377a796871687732376d753777376a736b30756e6b73787363617a3372616d7973386579667a7a6475636e6765657764357738707433687478666a66786b657a65757130326d6c6333646b636163716b736c716761356b33656c3064746775796771686a3976793572646b70746d7274633432776c3336667676346b6d6b66793735677377776b72796e3835656e6e32396d75396a6c35326e7730717a7967747a366879347a653535756e707071647133346371767237766374777132657a676c716432666b79676161657072746c66376b65707277763871307571383865657771746a756b6c6a3973713933323675717a76786b727837773772716e7a68633361777138367938796a3371386868633472356a6e3864637a7766613934763367307572716173706334727132636878767a3772776e6872683332386168326c6168707776656134747771"]
]

View File

@ -80,7 +80,7 @@ static SaplingPaymentAddress DefaultSaplingAddress(CWallet* pwallet) {
return usk.value()
.ToFullViewingKey()
.GetSaplingExtendedFullViewingKey().value()
.GetSaplingKey().value()
.FindAddress(libzcash::diversifier_index_t(0)).first;
}

View File

@ -5506,6 +5506,10 @@ KeyAddResult AddViewingKeyToWallet::operator()(const libzcash::SaplingExtendedFu
}
}
KeyAddResult AddViewingKeyToWallet::operator()(const libzcash::UnifiedFullViewingKey& no) const {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unified full viewing key import is not yet supported.");
}
KeyAddResult AddViewingKeyToWallet::operator()(const libzcash::InvalidEncoding& no) const {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid viewing key");
}

View File

@ -1481,6 +1481,7 @@ public:
KeyAddResult operator()(const libzcash::SproutViewingKey &sk) const;
KeyAddResult operator()(const libzcash::SaplingExtendedFullViewingKey &sk) const;
KeyAddResult operator()(const libzcash::UnifiedFullViewingKey &sk) const;
KeyAddResult operator()(const libzcash::InvalidEncoding& no) const;
};

View File

@ -1,6 +1,10 @@
#include "Address.hpp"
#include "zcash/address/unified.h"
#include "utilstrencodings.h"
#include <algorithm>
#include <iostream>
#include <rust/address.h>
const uint8_t ZCASH_UA_TYPECODE_P2PKH = 0x00;
const uint8_t ZCASH_UA_TYPECODE_P2SH = 0x01;
@ -51,6 +55,14 @@ std::pair<std::string, PaymentAddress> AddressInfoFromViewingKey::operator()(con
std::pair<std::string, PaymentAddress> AddressInfoFromViewingKey::operator()(const SaplingExtendedFullViewingKey &xfvk) const {
return std::make_pair("sapling", xfvk.DefaultAddress());
}
std::pair<std::string, PaymentAddress> AddressInfoFromViewingKey::operator()(const UnifiedFullViewingKey &ufvk) const {
return std::make_pair(
"unified",
ZcashdUnifiedFullViewingKey::FromUnifiedFullViewingKey(ufvk)
.FindAddress(diversifier_index_t(0))
.first
);
}
std::pair<std::string, PaymentAddress> AddressInfoFromViewingKey::operator()(const InvalidEncoding&) const {
throw std::invalid_argument("Cannot derive default address from invalid viewing key");
}
@ -176,3 +188,95 @@ std::set<libzcash::RawAddress> GetRawAddresses::operator()(
}
return ret;
}
std::optional<libzcash::UnifiedFullViewingKey> libzcash::UnifiedFullViewingKey::Decode(
const std::string& str,
const KeyConstants& keyConstants) {
UnifiedFullViewingKeyPtr* ptr = unified_full_viewing_key_parse(
keyConstants.NetworkIDString().c_str(),
str.c_str());
if (ptr == nullptr) {
return std::nullopt;
} else {
return UnifiedFullViewingKey(ptr);
}
}
std::string libzcash::UnifiedFullViewingKey::Encode(const KeyConstants& keyConstants) const {
auto encoded = unified_full_viewing_key_serialize(
keyConstants.NetworkIDString().c_str(),
inner.get());
// Copy the C-string into C++.
std::string res(encoded);
// Free the C-string.
zcash_address_string_free(encoded);
return res;
}
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())) {
CDataStream ss(buffer, SER_NETWORK, PROTOCOL_VERSION);
return SaplingDiversifiableFullViewingKey::Read(ss);
} else {
return std::nullopt;
}
}
std::optional<CChainablePubKey> libzcash::UnifiedFullViewingKey::GetTransparentKey() const {
std::vector<uint8_t> buffer(65);
if (unified_full_viewing_key_read_transparent(inner.get(), buffer.data())) {
CDataStream ss(buffer, SER_NETWORK, PROTOCOL_VERSION);
return CChainablePubKey::Read(ss);
} else {
return std::nullopt;
}
}
bool libzcash::UnifiedFullViewingKeyBuilder::AddTransparentKey(const CChainablePubKey& key) {
if (t_bytes.has_value()) return false;
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << key;
assert(ss.size() == 65);
std::vector<uint8_t> ss_bytes(ss.begin(), ss.end());
t_bytes = ss_bytes;
return true;
}
bool libzcash::UnifiedFullViewingKeyBuilder::AddSaplingKey(const SaplingDiversifiableFullViewingKey& key) {
if (sapling_bytes.has_value()) return false;
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << key;
assert(ss.size() == 128);
std::vector<uint8_t> ss_bytes(ss.begin(), ss.end());
sapling_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);
if (ptr == nullptr) {
return std::nullopt;
} else {
return UnifiedFullViewingKey(ptr);
}
}
libzcash::UnifiedFullViewingKey libzcash::UnifiedFullViewingKey::FromZcashdUFVK(const ZcashdUnifiedFullViewingKey& key) {
UnifiedFullViewingKeyBuilder builder;
if (key.GetTransparentKey().has_value()) {
builder.AddTransparentKey(key.GetTransparentKey().value());
}
if (key.GetSaplingKey().has_value()) {
builder.AddSaplingKey(key.GetSaplingKey().value());
}
auto result = builder.build();
if (!result.has_value()) {
throw std::invalid_argument("Cannot convert from invalid viewing key.");
}
return result.value();
}

View File

@ -1,15 +1,18 @@
#ifndef ZC_ADDRESS_H_
#define ZC_ADDRESS_H_
#include "uint256.h"
#include "key_constants.h"
#include "pubkey.h"
#include "script/script.h"
#include "uint256.h"
#include "zcash/address/orchard.hpp"
#include "zcash/address/sapling.hpp"
#include "zcash/address/sprout.hpp"
#include "zcash/address/unified.h"
#include "zcash/address/zip32.h"
#include <variant>
#include <rust/unified_keys.h>
namespace libzcash {
@ -127,14 +130,95 @@ public:
}
};
class UnifiedFullViewingKeyBuilder;
/**
* Wrapper for a zcash_address::unified::Ufvk.
*/
class UnifiedFullViewingKey {
private:
std::unique_ptr<UnifiedFullViewingKeyPtr, decltype(&unified_full_viewing_key_free)> inner;
UnifiedFullViewingKey() :
inner(nullptr, unified_full_viewing_key_free) {}
UnifiedFullViewingKey(UnifiedFullViewingKeyPtr* ptr) :
inner(ptr, unified_full_viewing_key_free) {}
friend class UnifiedFullViewingKeyBuilder;
public:
static std::optional<UnifiedFullViewingKey> Decode(
const std::string& str,
const KeyConstants& keyConstants);
/**
* This method should only be used for serialization of unified full
* viewing keys that have been generated internally from unified spending
* keys by Zcashd. It is not suitable for use in any case where the
* ZcashdUnifiedFullViewingKey value may have been produced by a
* potentially-lossy conversion from a UnifiedFullViewingKey value that
* originated outside of zcashd.
*/
static UnifiedFullViewingKey FromZcashdUFVK(const ZcashdUnifiedFullViewingKey&);
std::string Encode(const KeyConstants& keyConstants) const;
std::optional<SaplingDiversifiableFullViewingKey> GetSaplingKey() const;
std::optional<CChainablePubKey> GetTransparentKey() const;
UnifiedFullViewingKey(UnifiedFullViewingKey&& key) : inner(std::move(key.inner)) {}
UnifiedFullViewingKey(const UnifiedFullViewingKey& key) :
inner(unified_full_viewing_key_clone(key.inner.get()), unified_full_viewing_key_free) {}
UnifiedFullViewingKey& operator=(UnifiedFullViewingKey&& key)
{
if (this != &key) {
inner = std::move(key.inner);
}
return *this;
}
UnifiedFullViewingKey& operator=(const UnifiedFullViewingKey& key)
{
if (this != &key) {
inner.reset(unified_full_viewing_key_clone(key.inner.get()));
}
return *this;
}
};
class UnifiedFullViewingKeyBuilder {
private:
std::optional<std::vector<uint8_t>> t_bytes;
std::optional<std::vector<uint8_t>> sapling_bytes;
public:
UnifiedFullViewingKeyBuilder(): t_bytes(std::nullopt), sapling_bytes(std::nullopt) {}
bool AddTransparentKey(const CChainablePubKey&);
bool AddSaplingKey(const SaplingDiversifiableFullViewingKey&);
std::optional<UnifiedFullViewingKey> build() const;
};
/** Addresses that can appear in a string encoding. */
typedef std::variant<
InvalidEncoding,
SproutPaymentAddress,
SaplingPaymentAddress,
UnifiedAddress> PaymentAddress;
typedef std::variant<InvalidEncoding, SproutViewingKey, SaplingExtendedFullViewingKey> ViewingKey;
typedef std::variant<InvalidEncoding, SproutSpendingKey, SaplingExtendedSpendingKey> SpendingKey;
/** Viewing keys that can have a string encoding. */
typedef std::variant<
InvalidEncoding,
SproutViewingKey,
SaplingExtendedFullViewingKey,
UnifiedFullViewingKey> ViewingKey;
/** Spending keys that can have a string encoding. */
typedef std::variant<
InvalidEncoding,
SproutSpendingKey,
SaplingExtendedSpendingKey> SpendingKey;
class AddressInfoFromSpendingKey {
public:
@ -147,10 +231,11 @@ class AddressInfoFromViewingKey {
public:
std::pair<std::string, PaymentAddress> operator()(const SproutViewingKey&) const;
std::pair<std::string, PaymentAddress> operator()(const struct SaplingExtendedFullViewingKey&) const;
std::pair<std::string, PaymentAddress> operator()(const UnifiedFullViewingKey&) const;
std::pair<std::string, PaymentAddress> operator()(const InvalidEncoding&) const;
};
}
} //namespace libzcash
/** Check whether a PaymentAddress is not an InvalidEncoding. */
bool IsValidPaymentAddress(const libzcash::PaymentAddress& zaddr);

View File

@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
#include "zcash/Address.hpp"
#include "unified.h"
#include "bip44.h"
@ -29,7 +30,11 @@ ZcashdUnifiedFullViewingKey ZcashdUnifiedSpendingKey::ToFullViewingKey() const {
ZcashdUnifiedFullViewingKey ufvk;
if (transparentKey.has_value()) {
ufvk.transparentKey = transparentKey.value().Neuter();
auto extPubKey = transparentKey.value().Neuter();
// TODO: how to ensure that the pubkey is in its compressed representation?
// Is that already guaranteed?
ufvk.transparentKey = CChainablePubKey::FromParts(extPubKey.chaincode, extPubKey.pubkey).value();
}
if (saplingKey.has_value()) {
@ -39,6 +44,23 @@ ZcashdUnifiedFullViewingKey ZcashdUnifiedSpendingKey::ToFullViewingKey() const {
return ufvk;
}
ZcashdUnifiedFullViewingKey ZcashdUnifiedFullViewingKey::FromUnifiedFullViewingKey(
const UnifiedFullViewingKey& ufvk) {
ZcashdUnifiedFullViewingKey result;
auto transparentKey = ufvk.GetTransparentKey();
if (transparentKey.has_value()) {
result.transparentKey = transparentKey.value();
}
auto saplingKey = ufvk.GetSaplingKey();
if (saplingKey.has_value()) {
result.saplingKey = saplingKey.value();
}
return result;
}
std::optional<UnifiedAddress> ZcashdUnifiedFullViewingKey::Address(diversifier_index_t j) const {
UnifiedAddress ua;
@ -52,17 +74,20 @@ std::optional<UnifiedAddress> ZcashdUnifiedFullViewingKey::Address(diversifier_i
}
if (transparentKey.has_value()) {
const auto& tkey = transparentKey.value();
auto childIndex = j.ToTransparentChildIndex();
if (!childIndex.has_value()) return std::nullopt;
CExtPubKey externalKey;
if (!transparentKey.value().Derive(externalKey, 0)) {
CPubKey externalKey;
ChainCode externalChainCode;
if (!tkey.GetPubKey().Derive(externalKey, externalChainCode, 0, tkey.GetChainCode())) {
return std::nullopt;
}
CExtPubKey childKey;
if (externalKey.Derive(childKey, childIndex.value())) {
ua.AddReceiver(childKey.pubkey.GetID());
CPubKey childKey;
ChainCode childChainCode;
if (externalKey.Derive(childKey, childChainCode, childIndex.value(), externalChainCode)) {
ua.AddReceiver(childKey.GetID());
} else {
return std::nullopt;
}
@ -71,3 +96,13 @@ std::optional<UnifiedAddress> ZcashdUnifiedFullViewingKey::Address(diversifier_i
return ua;
}
std::pair<UnifiedAddress, diversifier_index_t> ZcashdUnifiedFullViewingKey::FindAddress(diversifier_index_t j) const {
auto addr = Address(j);
while (!addr.has_value()) {
if (!j.increment())
throw std::runtime_error(std::string(__func__) + ": diversifier index overflow.");;
addr = Address(j);
}
return std::make_pair(addr.value(), j);
}

View File

@ -7,43 +7,52 @@
#include "zip32.h"
#include "bip44.h"
#include "zcash/Address.hpp"
namespace libzcash {
class ZcashdUnifiedSpendingKey;
class ZcashdUnifiedFullViewingKey;
// prototypes for the classes handling ZIP-316 encoding (in Address.hpp)
class UnifiedAddress;
class UnifiedFullViewingKey;
/**
* An internal-only type for unified full viewing keys that represents only the
* set of receiver types that are supported by zcashd. This type does not
* support round-trip serialization to and from the UnifiedFullViewingKey type,
* which should be used in most cases.
*/
class ZcashdUnifiedFullViewingKey {
private:
std::optional<CExtPubKey> transparentKey;
std::optional<SaplingExtendedFullViewingKey> saplingKey;
std::optional<CChainablePubKey> transparentKey;
std::optional<SaplingDiversifiableFullViewingKey> saplingKey;
ZcashdUnifiedFullViewingKey() {}
friend class ZcashdUnifiedSpendingKey;
public:
const std::optional<CExtPubKey>& GetTransparentKey() const {
/**
* This constructor is lossy, and does not support round-trip transformations.
*/
static ZcashdUnifiedFullViewingKey FromUnifiedFullViewingKey(const UnifiedFullViewingKey& ufvk);
const std::optional<CChainablePubKey>& GetTransparentKey() const {
return transparentKey;
}
const std::optional<SaplingExtendedFullViewingKey>& GetSaplingExtendedFullViewingKey() const {
const std::optional<SaplingDiversifiableFullViewingKey>& GetSaplingKey() const {
return saplingKey;
}
std::optional<UnifiedAddress> Address(diversifier_index_t j) const;
std::pair<UnifiedAddress, diversifier_index_t> FindAddress(diversifier_index_t j) const {
auto addr = Address(j);
while (!addr.has_value()) {
if (!j.increment())
throw std::runtime_error(std::string(__func__) + ": diversifier index overflow.");;
addr = Address(j);
}
return std::make_pair(addr.value(), j);
}
std::pair<UnifiedAddress, diversifier_index_t> FindAddress(diversifier_index_t j) const;
};
/**
* The type of unified spending keys supported by zcashd.
*/
class ZcashdUnifiedSpendingKey {
private:
libzcash::AccountId accountId;

View File

@ -83,15 +83,16 @@ std::optional<SaplingExtendedFullViewingKey> SaplingExtendedFullViewingKey::Deri
}
std::optional<libzcash::SaplingPaymentAddress>
SaplingExtendedFullViewingKey::Address(diversifier_index_t j) const
SaplingDiversifiableFullViewingKey::Address(diversifier_index_t j) const
{
CDataStream ss_xfvk(SER_NETWORK, PROTOCOL_VERSION);
ss_xfvk << *this;
CSerializeData xfvk_bytes(ss_xfvk.begin(), ss_xfvk.end());
CDataStream ss_fvk(SER_NETWORK, PROTOCOL_VERSION);
ss_fvk << fvk;
CSerializeData fvk_bytes(ss_fvk.begin(), ss_fvk.end());
CSerializeData addr_bytes(libzcash::SerializedSaplingPaymentAddressSize);
if (librustzcash_zip32_xfvk_address(
reinterpret_cast<unsigned char*>(xfvk_bytes.data()),
if (librustzcash_zip32_sapling_address(
reinterpret_cast<unsigned char*>(fvk_bytes.data()),
dk.begin(),
j.begin(),
reinterpret_cast<unsigned char*>(addr_bytes.data()))) {
CDataStream ss_addr(addr_bytes, SER_NETWORK, PROTOCOL_VERSION);
@ -103,17 +104,18 @@ std::optional<libzcash::SaplingPaymentAddress>
}
}
libzcash::SaplingPaymentAddress SaplingExtendedFullViewingKey::DefaultAddress() const
libzcash::SaplingPaymentAddress SaplingDiversifiableFullViewingKey::DefaultAddress() const
{
CDataStream ss_xfvk(SER_NETWORK, PROTOCOL_VERSION);
ss_xfvk << *this;
CSerializeData xfvk_bytes(ss_xfvk.begin(), ss_xfvk.end());
CDataStream ss_fvk(SER_NETWORK, PROTOCOL_VERSION);
ss_fvk << fvk;
CSerializeData fvk_bytes(ss_fvk.begin(), ss_fvk.end());
diversifier_index_t j_default;
diversifier_index_t j_ret;
CSerializeData addr_bytes_ret(libzcash::SerializedSaplingPaymentAddressSize);
if (librustzcash_zip32_find_xfvk_address(
reinterpret_cast<unsigned char*>(xfvk_bytes.data()),
if (librustzcash_zip32_find_sapling_address(
reinterpret_cast<unsigned char*>(fvk_bytes.data()),
dk.begin(),
j_default.begin(), j_ret.begin(),
reinterpret_cast<unsigned char*>(addr_bytes_ret.data()))) {
CDataStream ss_addr(addr_bytes_ret, SER_NETWORK, PROTOCOL_VERSION);
@ -122,7 +124,7 @@ libzcash::SaplingPaymentAddress SaplingExtendedFullViewingKey::DefaultAddress()
return addr;
} else {
// If we can't obtain a default address, we are *very* unlucky...
throw std::runtime_error("SaplingExtendedFullViewingKey::DefaultAddress(): No valid diversifiers out of 2^88!");
throw std::runtime_error("SaplingDiversifiableFullViewingKey::DefaultAddress(): No valid diversifiers out of 2^88!");
}
}

View File

@ -117,28 +117,11 @@ public:
}
};
struct SaplingExtendedFullViewingKey {
uint8_t depth;
uint32_t parentFVKTag;
uint32_t childIndex;
uint256 chaincode;
class SaplingDiversifiableFullViewingKey {
public:
libzcash::SaplingFullViewingKey fvk;
uint256 dk;
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(depth);
READWRITE(parentFVKTag);
READWRITE(childIndex);
READWRITE(chaincode);
READWRITE(fvk);
READWRITE(dk);
}
std::optional<SaplingExtendedFullViewingKey> Derive(uint32_t i) const;
// Attempts to construct a valid payment address with diversifier index
// `j`; returns std::nullopt if `j` does not result in a valid diversifier
// given this xfvk.
@ -159,6 +142,48 @@ struct SaplingExtendedFullViewingKey {
libzcash::SaplingPaymentAddress DefaultAddress() const;
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(fvk);
READWRITE(dk);
}
template <typename Stream>
static SaplingDiversifiableFullViewingKey Read(Stream& stream) {
SaplingDiversifiableFullViewingKey key;
stream >> key;
return key;
}
friend inline bool operator==(const SaplingDiversifiableFullViewingKey& a, const SaplingDiversifiableFullViewingKey& b) {
return (a.fvk == b.fvk && a.dk == b.dk);
}
};
class SaplingExtendedFullViewingKey: public SaplingDiversifiableFullViewingKey {
public:
uint8_t depth;
uint32_t parentFVKTag;
uint32_t childIndex;
uint256 chaincode;
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(depth);
READWRITE(parentFVKTag);
READWRITE(childIndex);
READWRITE(chaincode);
READWRITE(fvk);
READWRITE(dk);
}
std::optional<SaplingExtendedFullViewingKey> Derive(uint32_t i) const;
friend inline bool operator==(const SaplingExtendedFullViewingKey& a, const SaplingExtendedFullViewingKey& b) {
return (
a.depth == b.depth &&