Add correct selection of change addresses to z_sendmany
This also alters `TransactionBuilder::SendChangeTo` to take a `libzcash::ChangeAddress` value and thus avoids the invalid t-addr case of CTxDestination.
This commit is contained in:
parent
992a47103d
commit
c21ffff790
|
@ -472,7 +472,7 @@ checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "equihash"
|
name = "equihash"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/zcash/librustzcash.git?rev=7801dddf35ca247345cf4f5c5e48791297cad531#7801dddf35ca247345cf4f5c5e48791297cad531"
|
source = "git+https://github.com/zcash/librustzcash.git?rev=a01290869a4f0c4e05bdcca550795ea0e76e8550#a01290869a4f0c4e05bdcca550795ea0e76e8550"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"blake2b_simd 1.0.0",
|
"blake2b_simd 1.0.0",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
|
@ -481,7 +481,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "f4jumble"
|
name = "f4jumble"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
source = "git+https://github.com/zcash/librustzcash.git?rev=7801dddf35ca247345cf4f5c5e48791297cad531#7801dddf35ca247345cf4f5c5e48791297cad531"
|
source = "git+https://github.com/zcash/librustzcash.git?rev=a01290869a4f0c4e05bdcca550795ea0e76e8550#a01290869a4f0c4e05bdcca550795ea0e76e8550"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"blake2b_simd 1.0.0",
|
"blake2b_simd 1.0.0",
|
||||||
]
|
]
|
||||||
|
@ -1841,10 +1841,9 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zcash_address"
|
name = "zcash_address"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
source = "git+https://github.com/zcash/librustzcash.git?rev=7801dddf35ca247345cf4f5c5e48791297cad531#7801dddf35ca247345cf4f5c5e48791297cad531"
|
source = "git+https://github.com/zcash/librustzcash.git?rev=a01290869a4f0c4e05bdcca550795ea0e76e8550#a01290869a4f0c4e05bdcca550795ea0e76e8550"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bech32",
|
"bech32",
|
||||||
"blake2b_simd 1.0.0",
|
|
||||||
"bs58",
|
"bs58",
|
||||||
"f4jumble",
|
"f4jumble",
|
||||||
"zcash_encoding",
|
"zcash_encoding",
|
||||||
|
@ -1853,7 +1852,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zcash_encoding"
|
name = "zcash_encoding"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
source = "git+https://github.com/zcash/librustzcash.git?rev=7801dddf35ca247345cf4f5c5e48791297cad531#7801dddf35ca247345cf4f5c5e48791297cad531"
|
source = "git+https://github.com/zcash/librustzcash.git?rev=a01290869a4f0c4e05bdcca550795ea0e76e8550#a01290869a4f0c4e05bdcca550795ea0e76e8550"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"nonempty",
|
"nonempty",
|
||||||
|
@ -1862,7 +1861,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zcash_history"
|
name = "zcash_history"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/zcash/librustzcash.git?rev=7801dddf35ca247345cf4f5c5e48791297cad531#7801dddf35ca247345cf4f5c5e48791297cad531"
|
source = "git+https://github.com/zcash/librustzcash.git?rev=a01290869a4f0c4e05bdcca550795ea0e76e8550#a01290869a4f0c4e05bdcca550795ea0e76e8550"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bigint",
|
"bigint",
|
||||||
"blake2b_simd 1.0.0",
|
"blake2b_simd 1.0.0",
|
||||||
|
@ -1872,7 +1871,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zcash_note_encryption"
|
name = "zcash_note_encryption"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/zcash/librustzcash.git?rev=7801dddf35ca247345cf4f5c5e48791297cad531#7801dddf35ca247345cf4f5c5e48791297cad531"
|
source = "git+https://github.com/zcash/librustzcash.git?rev=a01290869a4f0c4e05bdcca550795ea0e76e8550#a01290869a4f0c4e05bdcca550795ea0e76e8550"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chacha20",
|
"chacha20",
|
||||||
"chacha20poly1305",
|
"chacha20poly1305",
|
||||||
|
@ -1883,7 +1882,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zcash_primitives"
|
name = "zcash_primitives"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
source = "git+https://github.com/zcash/librustzcash.git?rev=7801dddf35ca247345cf4f5c5e48791297cad531#7801dddf35ca247345cf4f5c5e48791297cad531"
|
source = "git+https://github.com/zcash/librustzcash.git?rev=a01290869a4f0c4e05bdcca550795ea0e76e8550#a01290869a4f0c4e05bdcca550795ea0e76e8550"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes",
|
"aes",
|
||||||
"bip0039",
|
"bip0039",
|
||||||
|
@ -1901,11 +1900,9 @@ dependencies = [
|
||||||
"incrementalmerkletree",
|
"incrementalmerkletree",
|
||||||
"jubjub",
|
"jubjub",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
|
||||||
"memuse",
|
"memuse",
|
||||||
"nonempty",
|
"nonempty",
|
||||||
"orchard",
|
"orchard",
|
||||||
"pasta_curves",
|
|
||||||
"rand",
|
"rand",
|
||||||
"rand_core 0.6.3",
|
"rand_core 0.6.3",
|
||||||
"sha2",
|
"sha2",
|
||||||
|
@ -1917,7 +1914,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zcash_proofs"
|
name = "zcash_proofs"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
source = "git+https://github.com/zcash/librustzcash.git?rev=7801dddf35ca247345cf4f5c5e48791297cad531#7801dddf35ca247345cf4f5c5e48791297cad531"
|
source = "git+https://github.com/zcash/librustzcash.git?rev=a01290869a4f0c4e05bdcca550795ea0e76e8550#a01290869a4f0c4e05bdcca550795ea0e76e8550"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bellman",
|
"bellman",
|
||||||
"blake2b_simd 1.0.0",
|
"blake2b_simd 1.0.0",
|
||||||
|
|
10
Cargo.toml
10
Cargo.toml
|
@ -71,8 +71,8 @@ panic = 'abort'
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "7801dddf35ca247345cf4f5c5e48791297cad531" }
|
zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "a01290869a4f0c4e05bdcca550795ea0e76e8550" }
|
||||||
zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "7801dddf35ca247345cf4f5c5e48791297cad531" }
|
zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "a01290869a4f0c4e05bdcca550795ea0e76e8550" }
|
||||||
zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "7801dddf35ca247345cf4f5c5e48791297cad531" }
|
zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "a01290869a4f0c4e05bdcca550795ea0e76e8550" }
|
||||||
zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "7801dddf35ca247345cf4f5c5e48791297cad531" }
|
zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "a01290869a4f0c4e05bdcca550795ea0e76e8550" }
|
||||||
zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "7801dddf35ca247345cf4f5c5e48791297cad531" }
|
zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "a01290869a4f0c4e05bdcca550795ea0e76e8550" }
|
||||||
|
|
|
@ -268,17 +268,6 @@ TEST(TransactionBuilder, RejectsInvalidTransparentOutput)
|
||||||
ASSERT_THROW(builder.AddTransparentOutput(taddr, 50), UniValue);
|
ASSERT_THROW(builder.AddTransparentOutput(taddr, 50), UniValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(TransactionBuilder, RejectsInvalidTransparentChangeAddress)
|
|
||||||
{
|
|
||||||
SelectParams(CBaseChainParams::REGTEST);
|
|
||||||
const Consensus::Params& consensusParams = Params().GetConsensus();
|
|
||||||
|
|
||||||
// Default CTxDestination type is an invalid address
|
|
||||||
CTxDestination taddr;
|
|
||||||
auto builder = TransactionBuilder(consensusParams, 1);
|
|
||||||
ASSERT_THROW(builder.SendChangeTo(taddr), UniValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(TransactionBuilder, FailsWithNegativeChange)
|
TEST(TransactionBuilder, FailsWithNegativeChange)
|
||||||
{
|
{
|
||||||
auto consensusParams = RegtestActivateSapling();
|
auto consensusParams = RegtestActivateSapling();
|
||||||
|
@ -344,7 +333,6 @@ TEST(TransactionBuilder, ChangeOutput)
|
||||||
CKey tsk = AddTestCKeyToKeyStore(keystore);
|
CKey tsk = AddTestCKeyToKeyStore(keystore);
|
||||||
auto tkeyid = tsk.GetPubKey().GetID();
|
auto tkeyid = tsk.GetPubKey().GetID();
|
||||||
auto scriptPubKey = GetScriptForDestination(tkeyid);
|
auto scriptPubKey = GetScriptForDestination(tkeyid);
|
||||||
CTxDestination taddr = tkeyid;
|
|
||||||
|
|
||||||
// No change address and no Sapling spends
|
// No change address and no Sapling spends
|
||||||
{
|
{
|
||||||
|
@ -387,7 +375,7 @@ TEST(TransactionBuilder, ChangeOutput)
|
||||||
{
|
{
|
||||||
auto builder = TransactionBuilder(consensusParams, 1, &keystore);
|
auto builder = TransactionBuilder(consensusParams, 1, &keystore);
|
||||||
builder.AddTransparentInput(COutPoint(), scriptPubKey, 25000);
|
builder.AddTransparentInput(COutPoint(), scriptPubKey, 25000);
|
||||||
builder.SendChangeTo(taddr);
|
builder.SendChangeTo(tkeyid, {});
|
||||||
auto tx = builder.Build().GetTxOrThrow();
|
auto tx = builder.Build().GetTxOrThrow();
|
||||||
|
|
||||||
EXPECT_EQ(tx.vin.size(), 1);
|
EXPECT_EQ(tx.vin.size(), 1);
|
||||||
|
|
|
@ -306,6 +306,17 @@ extern "C" {
|
||||||
unsigned char *xfvk_i
|
unsigned char *xfvk_i
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Derive the Sapling FVK for an internal BIP44 chain from the
|
||||||
|
* corresponding external chain FVK.
|
||||||
|
*/
|
||||||
|
void librustzcash_zip32_sapling_derive_internal_fvk(
|
||||||
|
const unsigned char *fvk,
|
||||||
|
const unsigned char *dk,
|
||||||
|
unsigned char *fvk_ret,
|
||||||
|
unsigned char *dk_ret
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Derive a PaymentAddress from a (SaplingFullViewingKey, DiversifierKey)
|
* Derive a PaymentAddress from a (SaplingFullViewingKey, DiversifierKey)
|
||||||
* pair. Returns 'false' if no valid address can be derived for the
|
* pair. Returns 'false' if no valid address can be derived for the
|
||||||
|
|
|
@ -55,7 +55,7 @@ use zcash_primitives::{
|
||||||
},
|
},
|
||||||
sapling::{merkle_hash, spend_sig},
|
sapling::{merkle_hash, spend_sig},
|
||||||
transaction::components::Amount,
|
transaction::components::Amount,
|
||||||
zip32::{self, sapling_address, sapling_find_address},
|
zip32::{self, sapling_address, sapling_derive_internal_fvk, sapling_find_address},
|
||||||
};
|
};
|
||||||
use zcash_proofs::{
|
use zcash_proofs::{
|
||||||
circuit::sapling::TREE_DEPTH as SAPLING_TREE_DEPTH,
|
circuit::sapling::TREE_DEPTH as SAPLING_TREE_DEPTH,
|
||||||
|
@ -1081,6 +1081,25 @@ pub extern "C" fn librustzcash_zip32_xfvk_derive(
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Derive the Sapling internal
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn librustzcash_zip32_sapling_derive_internal_fvk(
|
||||||
|
fvk: *const [c_uchar; 96],
|
||||||
|
dk: *const [c_uchar; 32],
|
||||||
|
fvk_ret: *mut [c_uchar; 96],
|
||||||
|
dk_ret: *mut [c_uchar; 32],
|
||||||
|
) {
|
||||||
|
let fvk = FullViewingKey::read(&unsafe { *fvk }[..]).expect("valid Sapling FullViewingKey");
|
||||||
|
let dk = zip32::DiversifierKey(unsafe { *dk });
|
||||||
|
|
||||||
|
let (fvk_internal, dk_internal) = sapling_derive_internal_fvk(&fvk, &dk);
|
||||||
|
let fvk_ret = unsafe { &mut *fvk_ret };
|
||||||
|
let dk_ret = unsafe { &mut *dk_ret };
|
||||||
|
|
||||||
|
fvk_ret.copy_from_slice(&fvk_internal.to_bytes());
|
||||||
|
dk_ret.copy_from_slice(&dk_internal.0);
|
||||||
|
}
|
||||||
|
|
||||||
/// Derive a PaymentAddress from an ExtendedFullViewingKey.
|
/// Derive a PaymentAddress from an ExtendedFullViewingKey.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn librustzcash_zip32_sapling_address(
|
pub extern "C" fn librustzcash_zip32_sapling_address(
|
||||||
|
|
|
@ -280,29 +280,31 @@ void TransactionBuilder::SetFee(CAmount fee)
|
||||||
this->fee = fee;
|
this->fee = fee;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransactionBuilder::SendChangeTo(libzcash::SaplingPaymentAddress changeAddr, uint256 ovk)
|
// TODO: remove support for transparent change?
|
||||||
{
|
void TransactionBuilder::SendChangeTo(
|
||||||
saplingChangeAddr = std::make_pair(ovk, changeAddr);
|
const libzcash::RecipientAddress& changeAddr,
|
||||||
sproutChangeAddr = std::nullopt;
|
const uint256& ovk) {
|
||||||
tChangeAddr = std::nullopt;
|
tChangeAddr = std::nullopt;
|
||||||
}
|
|
||||||
|
|
||||||
void TransactionBuilder::SendChangeTo(libzcash::SproutPaymentAddress changeAddr)
|
|
||||||
{
|
|
||||||
sproutChangeAddr = changeAddr;
|
|
||||||
saplingChangeAddr = std::nullopt;
|
|
||||||
tChangeAddr = std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TransactionBuilder::SendChangeTo(CTxDestination& changeAddr)
|
|
||||||
{
|
|
||||||
if (!IsValidDestination(changeAddr)) {
|
|
||||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid change address, not a valid taddr.");
|
|
||||||
}
|
|
||||||
|
|
||||||
tChangeAddr = changeAddr;
|
|
||||||
saplingChangeAddr = std::nullopt;
|
saplingChangeAddr = std::nullopt;
|
||||||
sproutChangeAddr = std::nullopt;
|
sproutChangeAddr = std::nullopt;
|
||||||
|
|
||||||
|
std::visit(match {
|
||||||
|
[&](const CKeyID& keyId) {
|
||||||
|
tChangeAddr = keyId;
|
||||||
|
},
|
||||||
|
[&](const CScriptID& scriptId) {
|
||||||
|
tChangeAddr = scriptId;
|
||||||
|
},
|
||||||
|
[&](const libzcash::SaplingPaymentAddress& changeDest) {
|
||||||
|
saplingChangeAddr = std::make_pair(ovk, changeDest);
|
||||||
|
}
|
||||||
|
}, changeAddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransactionBuilder::SendChangeToSprout(const libzcash::SproutPaymentAddress& zaddr) {
|
||||||
|
tChangeAddr = std::nullopt;
|
||||||
|
saplingChangeAddr = std::nullopt;
|
||||||
|
sproutChangeAddr = zaddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
TransactionBuilderResult TransactionBuilder::Build()
|
TransactionBuilderResult TransactionBuilder::Build()
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include "script/script.h"
|
#include "script/script.h"
|
||||||
#include "script/standard.h"
|
#include "script/standard.h"
|
||||||
#include "uint256.h"
|
#include "uint256.h"
|
||||||
|
#include "util/match.h"
|
||||||
#include "zcash/Address.hpp"
|
#include "zcash/Address.hpp"
|
||||||
#include "zcash/IncrementalMerkleTree.hpp"
|
#include "zcash/IncrementalMerkleTree.hpp"
|
||||||
#include "zcash/JoinSplit.hpp"
|
#include "zcash/JoinSplit.hpp"
|
||||||
|
@ -170,11 +171,8 @@ public:
|
||||||
|
|
||||||
void AddTransparentOutput(const CTxDestination& to, CAmount value);
|
void AddTransparentOutput(const CTxDestination& to, CAmount value);
|
||||||
|
|
||||||
void SendChangeTo(libzcash::SaplingPaymentAddress changeAddr, uint256 ovk);
|
void SendChangeTo(const libzcash::RecipientAddress& changeAddr, const uint256& ovk);
|
||||||
|
void SendChangeToSprout(const libzcash::SproutPaymentAddress& changeAddr);
|
||||||
void SendChangeTo(libzcash::SproutPaymentAddress);
|
|
||||||
|
|
||||||
void SendChangeTo(CTxDestination& changeAddr);
|
|
||||||
|
|
||||||
TransactionBuilderResult Build();
|
TransactionBuilderResult Build();
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
extern UniValue signrawtransaction(const UniValue& params, bool fHelp);
|
extern UniValue signrawtransaction(const UniValue& params, bool fHelp);
|
||||||
|
|
||||||
|
// TODO: instead of passing a TestMode flag, tests should override `CommitTransaction`
|
||||||
|
// on the wallet.
|
||||||
UniValue SendTransaction(CTransaction& tx, std::optional<std::reference_wrapper<CReserveKey>> reservekey, bool testmode) {
|
UniValue SendTransaction(CTransaction& tx, std::optional<std::reference_wrapper<CReserveKey>> reservekey, bool testmode) {
|
||||||
UniValue o(UniValue::VOBJ);
|
UniValue o(UniValue::VOBJ);
|
||||||
// Send the transaction
|
// Send the transaction
|
||||||
|
|
|
@ -60,31 +60,6 @@ AsyncRPCOperation_sendmany::AsyncRPCOperation_sendmany(
|
||||||
assert(!recipients_.empty());
|
assert(!recipients_.empty());
|
||||||
assert(ztxoSelector.RequireSpendingKeys());
|
assert(ztxoSelector.RequireSpendingKeys());
|
||||||
|
|
||||||
std::visit(match {
|
|
||||||
[&](const AccountZTXOPattern& acct) {
|
|
||||||
isfromtaddr_ =
|
|
||||||
acct.GetReceiverTypes().empty() ||
|
|
||||||
acct.GetReceiverTypes().count(ReceiverType::P2PKH) > 0 ||
|
|
||||||
acct.GetReceiverTypes().count(ReceiverType::P2SH) > 0;
|
|
||||||
},
|
|
||||||
[&](const CKeyID& keyId) {
|
|
||||||
isfromtaddr_ = true;
|
|
||||||
},
|
|
||||||
[&](const CScriptID& scriptId) {
|
|
||||||
isfromtaddr_ = true;
|
|
||||||
},
|
|
||||||
[&](const libzcash::SproutPaymentAddress& addr) {
|
|
||||||
isfromsprout_ = true;
|
|
||||||
},
|
|
||||||
[&](const libzcash::SaplingPaymentAddress& addr) {
|
|
||||||
isfromsapling_ = true;
|
|
||||||
}
|
|
||||||
}, ztxoSelector.GetPattern());
|
|
||||||
|
|
||||||
if ((isfromsprout_ || isfromsapling_) && minDepth == 0) {
|
|
||||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Minconf cannot be zero when sending from a shielded address");
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculate the target totals
|
// calculate the target totals
|
||||||
for (const SendManyRecipient& recipient : recipients_) {
|
for (const SendManyRecipient& recipient : recipients_) {
|
||||||
std::visit(match {
|
std::visit(match {
|
||||||
|
@ -98,7 +73,7 @@ AsyncRPCOperation_sendmany::AsyncRPCOperation_sendmany(
|
||||||
},
|
},
|
||||||
[&](const libzcash::SaplingPaymentAddress& addr) {
|
[&](const libzcash::SaplingPaymentAddress& addr) {
|
||||||
txOutputAmounts_.z_outputs_total += recipient.amount;
|
txOutputAmounts_.z_outputs_total += recipient.amount;
|
||||||
if (isfromsprout_ && !allowRevealedAmounts_) {
|
if (ztxoSelector_.SelectsSprout() && !allowRevealedAmounts_) {
|
||||||
throw JSONRPCError(
|
throw JSONRPCError(
|
||||||
RPC_INVALID_PARAMETER,
|
RPC_INVALID_PARAMETER,
|
||||||
"Sending between shielded pools is not enabled by default because it will "
|
"Sending between shielded pools is not enabled by default because it will "
|
||||||
|
@ -192,10 +167,6 @@ void AsyncRPCOperation_sendmany::main() {
|
||||||
//
|
//
|
||||||
// At least 4. and 5. differ from the Rust transaction builder.
|
// At least 4. and 5. differ from the Rust transaction builder.
|
||||||
uint256 AsyncRPCOperation_sendmany::main_impl() {
|
uint256 AsyncRPCOperation_sendmany::main_impl() {
|
||||||
// TODO UA: this check will become meaningless.
|
|
||||||
bool isfromzaddr_ = isfromsprout_ || isfromsapling_;
|
|
||||||
assert(isfromtaddr_ != isfromzaddr_);
|
|
||||||
|
|
||||||
CAmount sendAmount = txOutputAmounts_.z_outputs_total + txOutputAmounts_.t_outputs_total;
|
CAmount sendAmount = txOutputAmounts_.z_outputs_total + txOutputAmounts_.t_outputs_total;
|
||||||
CAmount targetAmount = sendAmount + fee_;
|
CAmount targetAmount = sendAmount + fee_;
|
||||||
|
|
||||||
|
@ -245,14 +216,6 @@ uint256 AsyncRPCOperation_sendmany::main_impl() {
|
||||||
|
|
||||||
spendable.LogInputs(getId());
|
spendable.LogInputs(getId());
|
||||||
|
|
||||||
// At least one of z_sprout_inputs_ and z_sapling_inputs_ must be empty by design
|
|
||||||
//
|
|
||||||
// TODO: This restriction is true by construction as we have no mechanism
|
|
||||||
// for filtering for notes that will select both Sprout and Sapling notes
|
|
||||||
// simultaneously, but even if we did it would likely be safe to remove
|
|
||||||
// this limitation.
|
|
||||||
assert(spendable.sproutNoteEntries.empty() || spendable.saplingNoteEntries.empty());
|
|
||||||
|
|
||||||
CAmount t_inputs_total{0};
|
CAmount t_inputs_total{0};
|
||||||
CAmount z_inputs_total{0};
|
CAmount z_inputs_total{0};
|
||||||
for (const auto& t : spendable.utxos) {
|
for (const auto& t : spendable.utxos) {
|
||||||
|
@ -265,25 +228,16 @@ uint256 AsyncRPCOperation_sendmany::main_impl() {
|
||||||
z_inputs_total += t.note.value();
|
z_inputs_total += t.note.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO UA: these restrictions should be removed.
|
if (z_inputs_total > 0 && mindepth_ == 0) {
|
||||||
assert(!isfromtaddr_ || z_inputs_total == 0);
|
throw JSONRPCError(
|
||||||
assert(!isfromzaddr_ || t_inputs_total == 0);
|
RPC_INVALID_PARAMETER,
|
||||||
|
"Minconf cannot be zero when sending from a shielded address");
|
||||||
if (isfromtaddr_ && (t_inputs_total < targetAmount)) {
|
|
||||||
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS,
|
|
||||||
strprintf("Insufficient transparent funds, have %s, need %s",
|
|
||||||
FormatMoney(t_inputs_total), FormatMoney(targetAmount)));
|
|
||||||
}
|
|
||||||
if (isfromzaddr_ && (z_inputs_total < targetAmount)) {
|
|
||||||
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS,
|
|
||||||
strprintf("Insufficient shielded funds, have %s, need %s",
|
|
||||||
FormatMoney(z_inputs_total), FormatMoney(targetAmount)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// When spending transparent coinbase outputs, all inputs must be fully
|
// When spending transparent coinbase outputs, all inputs must be fully
|
||||||
// consumed, and they may only be sent to shielded recipients.
|
// consumed, and they may only be sent to shielded recipients.
|
||||||
if (spendable.HasTransparentCoinbase()) {
|
if (spendable.HasTransparentCoinbase()) {
|
||||||
if (t_inputs_total != targetAmount) {
|
if (t_inputs_total + z_inputs_total != targetAmount) {
|
||||||
throw JSONRPCError(
|
throw JSONRPCError(
|
||||||
RPC_WALLET_ERROR,
|
RPC_WALLET_ERROR,
|
||||||
strprintf(
|
strprintf(
|
||||||
|
@ -299,66 +253,55 @@ uint256 AsyncRPCOperation_sendmany::main_impl() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isfromtaddr_) {
|
LogPrint("zrpcunsafe", "%s: spending %s to send %s with fee %s\n",
|
||||||
LogPrint("zrpc", "%s: spending %s to send %s with fee %s\n",
|
getId(), FormatMoney(targetAmount), FormatMoney(sendAmount), FormatMoney(fee_));
|
||||||
getId(), FormatMoney(targetAmount), FormatMoney(sendAmount), FormatMoney(fee_));
|
|
||||||
} else {
|
|
||||||
LogPrint("zrpcunsafe", "%s: spending %s to send %s with fee %s\n",
|
|
||||||
getId(), FormatMoney(targetAmount), FormatMoney(sendAmount), FormatMoney(fee_));
|
|
||||||
}
|
|
||||||
LogPrint("zrpc", "%s: transparent input: %s (to choose from)\n", getId(), FormatMoney(t_inputs_total));
|
LogPrint("zrpc", "%s: transparent input: %s (to choose from)\n", getId(), FormatMoney(t_inputs_total));
|
||||||
LogPrint("zrpcunsafe", "%s: private input: %s (to choose from)\n", getId(), FormatMoney(z_inputs_total));
|
LogPrint("zrpcunsafe", "%s: private input: %s (to choose from)\n", getId(), FormatMoney(z_inputs_total));
|
||||||
LogPrint("zrpc", "%s: transparent output: %s\n", getId(), FormatMoney(txOutputAmounts_.t_outputs_total));
|
LogPrint("zrpc", "%s: transparent output: %s\n", getId(), FormatMoney(txOutputAmounts_.t_outputs_total));
|
||||||
LogPrint("zrpcunsafe", "%s: private output: %s\n", getId(), FormatMoney(txOutputAmounts_.z_outputs_total));
|
LogPrint("zrpcunsafe", "%s: private output: %s\n", getId(), FormatMoney(txOutputAmounts_.z_outputs_total));
|
||||||
LogPrint("zrpc", "%s: fee: %s\n", getId(), FormatMoney(fee_));
|
LogPrint("zrpc", "%s: fee: %s\n", getId(), FormatMoney(fee_));
|
||||||
|
|
||||||
CReserveKey keyChange(pwalletMain);
|
auto ovks = this->SelectOVKs();
|
||||||
uint256 ovk;
|
auto selectorAccountId = pwalletMain->FindAccountForSelector(ztxoSelector_);
|
||||||
|
|
||||||
auto getDefaultOVK = [&]() {
|
|
||||||
HDSeed seed = pwalletMain->GetHDSeedForRPC();
|
|
||||||
return ovkForShieldingFromTaddr(seed);
|
|
||||||
};
|
|
||||||
|
|
||||||
auto setTransparentChangeRecipient = [&]() {
|
|
||||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
|
||||||
|
|
||||||
EnsureWalletIsUnlocked();
|
|
||||||
CPubKey vchPubKey;
|
|
||||||
bool ret = keyChange.GetReservedKey(vchPubKey);
|
|
||||||
if (!ret) {
|
|
||||||
// should never fail, as we just unlocked
|
|
||||||
throw JSONRPCError(
|
|
||||||
RPC_WALLET_KEYPOOL_RAN_OUT,
|
|
||||||
"Could not generate a taddr to use as a change address");
|
|
||||||
}
|
|
||||||
|
|
||||||
CTxDestination changeAddr = vchPubKey.GetID();
|
|
||||||
builder_.SendChangeTo(changeAddr);
|
|
||||||
};
|
|
||||||
|
|
||||||
// FIXME: use the appropriate shielded pool change address for the
|
|
||||||
// source unified address account (or the legacy account), and the
|
|
||||||
// associated OVK
|
|
||||||
std::visit(match {
|
std::visit(match {
|
||||||
|
[&](const CKeyID& keyId) {
|
||||||
|
auto accountId = selectorAccountId.value_or(ZCASH_LEGACY_ACCOUNT);
|
||||||
|
builder_.SendChangeTo(
|
||||||
|
pwalletMain->GenerateChangeAddressForAccount(accountId, { libzcash::ChangeType::Sapling }).value(),
|
||||||
|
ovks.first);
|
||||||
|
},
|
||||||
|
[&](const CScriptID& scriptId) {
|
||||||
|
auto accountId = selectorAccountId.value_or(ZCASH_LEGACY_ACCOUNT);
|
||||||
|
builder_.SendChangeTo(
|
||||||
|
pwalletMain->GenerateChangeAddressForAccount(accountId, { libzcash::ChangeType::Sapling }).value(),
|
||||||
|
ovks.first);
|
||||||
|
},
|
||||||
[&](const libzcash::SproutPaymentAddress& addr) {
|
[&](const libzcash::SproutPaymentAddress& addr) {
|
||||||
ovk = getDefaultOVK();
|
// for Sprout, we return change to the originating address.
|
||||||
builder_.SendChangeTo(addr);
|
builder_.SendChangeToSprout(addr);
|
||||||
},
|
},
|
||||||
[&](const libzcash::SaplingPaymentAddress& addr) {
|
[&](const libzcash::SaplingPaymentAddress& addr) {
|
||||||
libzcash::SaplingExtendedSpendingKey saplingKey;
|
// for Sapling, if using a legacy address, return change to the
|
||||||
assert(pwalletMain->GetSaplingExtendedSpendingKey(addr, saplingKey));
|
// originating address; otherwise return it to the Sapling internal
|
||||||
|
// address corresponding to the UFVK.
|
||||||
ovk = saplingKey.expsk.full_viewing_key().ovk;
|
if (selectorAccountId.has_value()) {
|
||||||
builder_.SendChangeTo(addr, ovk);
|
auto changeAddr = pwalletMain->GenerateChangeAddressForAccount(
|
||||||
|
selectorAccountId.value(), { libzcash::ChangeType::Sapling }).value();
|
||||||
|
builder_.SendChangeTo(changeAddr, ovks.first);
|
||||||
|
} else {
|
||||||
|
builder_.SendChangeTo(addr, ovks.first);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[&](const auto& other) {
|
[&](const AccountZTXOPattern& acct) {
|
||||||
ovk = getDefaultOVK();
|
auto changeAddr = pwalletMain->GenerateChangeAddressForAccount(
|
||||||
setTransparentChangeRecipient();
|
selectorAccountId.value(), { libzcash::ChangeType::Sapling });
|
||||||
|
assert(changeAddr.has_value());
|
||||||
|
builder_.SendChangeTo(changeAddr.value(), ovks.first);
|
||||||
}
|
}
|
||||||
}, ztxoSelector_.GetPattern());
|
}, ztxoSelector_.GetPattern());
|
||||||
|
|
||||||
// Track the total of notes that we've added to the builder
|
// Track the total of notes that we've added to the builder. This
|
||||||
|
// shouldn't strictly be necessary, given `spendable.LimitToAmount`
|
||||||
CAmount sum = 0;
|
CAmount sum = 0;
|
||||||
|
|
||||||
// Create Sapling outpoints
|
// Create Sapling outpoints
|
||||||
|
@ -410,7 +353,7 @@ uint256 AsyncRPCOperation_sendmany::main_impl() {
|
||||||
auto value = r.amount;
|
auto value = r.amount;
|
||||||
auto memo = get_memo_from_hex_string(r.memo.has_value() ? r.memo.value() : "");
|
auto memo = get_memo_from_hex_string(r.memo.has_value() ? r.memo.value() : "");
|
||||||
|
|
||||||
builder_.AddSaplingOutput(ovk, addr, value, memo);
|
builder_.AddSaplingOutput(ovks.second, addr, value, memo);
|
||||||
}
|
}
|
||||||
}, r.address);
|
}, r.address);
|
||||||
}
|
}
|
||||||
|
@ -466,12 +409,20 @@ uint256 AsyncRPCOperation_sendmany::main_impl() {
|
||||||
auto buildResult = builder_.Build();
|
auto buildResult = builder_.Build();
|
||||||
auto tx = buildResult.GetTxOrThrow();
|
auto tx = buildResult.GetTxOrThrow();
|
||||||
|
|
||||||
UniValue sendResult = SendTransaction(tx, keyChange, testmode);
|
UniValue sendResult = SendTransaction(tx, std::nullopt, testmode);
|
||||||
set_result(sendResult);
|
set_result(sendResult);
|
||||||
|
|
||||||
return tx.GetHash();
|
return tx.GetHash();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<uint256, uint256> AsyncRPCOperation_sendmany::SelectOVKs() const {
|
||||||
|
//TODO
|
||||||
|
uint256 internalOVK;
|
||||||
|
uint256 externalOVK;
|
||||||
|
|
||||||
|
return std::make_pair(internalOVK, externalOVK);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compute a dust threshold based upon a standard p2pkh txout.
|
* Compute a dust threshold based upon a standard p2pkh txout.
|
||||||
*/
|
*/
|
||||||
|
@ -523,4 +474,3 @@ UniValue AsyncRPCOperation_sendmany::getStatus() const {
|
||||||
obj.pushKV("params", contextinfo_ );
|
obj.pushKV("params", contextinfo_ );
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,13 +75,19 @@ private:
|
||||||
CAmount fee_;
|
CAmount fee_;
|
||||||
UniValue contextinfo_; // optional data to include in return value from getStatus()
|
UniValue contextinfo_; // optional data to include in return value from getStatus()
|
||||||
|
|
||||||
bool isfromtaddr_{false};
|
|
||||||
bool isfromsprout_{false};
|
bool isfromsprout_{false};
|
||||||
bool isfromsapling_{false};
|
bool isfromsapling_{false};
|
||||||
bool allowRevealedAmounts_{false};
|
bool allowRevealedAmounts_{false};
|
||||||
uint32_t transparentRecipients_{0};
|
uint32_t transparentRecipients_{0};
|
||||||
TxOutputAmounts txOutputAmounts_;
|
TxOutputAmounts txOutputAmounts_;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the internal and external OVKs to use in transaction construction, given
|
||||||
|
* the payment source and the set of types that correspond to outputs selected for
|
||||||
|
* being spent in the transaction.
|
||||||
|
*/
|
||||||
|
std::pair<uint256, uint256> SelectOVKs() const;
|
||||||
|
|
||||||
static CAmount DefaultDustThreshold();
|
static CAmount DefaultDustThreshold();
|
||||||
|
|
||||||
static std::array<unsigned char, ZC_MEMO_SIZE> get_memo_from_hex_string(std::string s);
|
static std::array<unsigned char, ZC_MEMO_SIZE> get_memo_from_hex_string(std::string s);
|
||||||
|
@ -89,7 +95,6 @@ private:
|
||||||
uint256 main_impl();
|
uint256 main_impl();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// To test private methods, a friend class can act as a proxy
|
// To test private methods, a friend class can act as a proxy
|
||||||
class TEST_FRIEND_AsyncRPCOperation_sendmany {
|
class TEST_FRIEND_AsyncRPCOperation_sendmany {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -258,6 +258,7 @@ bool ShieldToAddress::operator()(const libzcash::SaplingPaymentAddress &zaddr) c
|
||||||
// generate a common one from the HD seed. This ensures the data is
|
// generate a common one from the HD seed. This ensures the data is
|
||||||
// recoverable, while keeping it logically separate from the ZIP 32
|
// recoverable, while keeping it logically separate from the ZIP 32
|
||||||
// Sapling key hierarchy, which the user might not be using.
|
// Sapling key hierarchy, which the user might not be using.
|
||||||
|
// FIXME: update to use the ZIP-316 OVK
|
||||||
HDSeed seed = pwalletMain->GetHDSeedForRPC();
|
HDSeed seed = pwalletMain->GetHDSeedForRPC();
|
||||||
uint256 ovk = ovkForShieldingFromTaddr(seed);
|
uint256 ovk = ovkForShieldingFromTaddr(seed);
|
||||||
|
|
||||||
|
|
|
@ -2199,7 +2199,7 @@ TEST(WalletTests, GenerateUnifiedAddress) {
|
||||||
ua->first.GetSaplingReceiver(),
|
ua->first.GetSaplingReceiver(),
|
||||||
ufvk.GetSaplingKey().value().Address(ua->second));
|
ufvk.GetSaplingKey().value().Address(ua->second));
|
||||||
|
|
||||||
auto u4r = wallet.GetUnifiedForReceiver(ua->first.GetSaplingReceiver().value());
|
auto u4r = wallet.FindUnifiedAddressByReceiver(ua->first.GetSaplingReceiver().value());
|
||||||
EXPECT_EQ(u4r, ua->first);
|
EXPECT_EQ(u4r, ua->first);
|
||||||
|
|
||||||
// Explicitly trigger the invalid transparent child index failure
|
// Explicitly trigger the invalid transparent child index failure
|
||||||
|
|
|
@ -4044,7 +4044,7 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp)
|
||||||
// If the note belongs to a Sapling address that is part of an account in the
|
// If the note belongs to a Sapling address that is part of an account in the
|
||||||
// wallet, show the corresponding Unified Address.
|
// wallet, show the corresponding Unified Address.
|
||||||
std::string address;
|
std::string address;
|
||||||
const auto ua = pwalletMain->GetUnifiedForReceiver(pa);
|
const auto ua = pwalletMain->FindUnifiedAddressByReceiver(pa);
|
||||||
if (ua.has_value()) {
|
if (ua.has_value()) {
|
||||||
address = keyIO.EncodePaymentAddress(ua.value());
|
address = keyIO.EncodePaymentAddress(ua.value());
|
||||||
} else {
|
} else {
|
||||||
|
@ -4097,7 +4097,7 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp)
|
||||||
// If the note belongs to a Sapling address that is part of an account in the
|
// If the note belongs to a Sapling address that is part of an account in the
|
||||||
// wallet, show the corresponding Unified Address.
|
// wallet, show the corresponding Unified Address.
|
||||||
std::string address;
|
std::string address;
|
||||||
const auto ua = pwalletMain->GetUnifiedForReceiver(pa);
|
const auto ua = pwalletMain->FindUnifiedAddressByReceiver(pa);
|
||||||
if (ua.has_value()) {
|
if (ua.has_value()) {
|
||||||
address = keyIO.EncodePaymentAddress(ua.value());
|
address = keyIO.EncodePaymentAddress(ua.value());
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -536,7 +536,7 @@ bool CWallet::AddUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &uf
|
||||||
return CWalletDB(strWalletFile).WriteUnifiedFullViewingKey(ufvk);
|
return CWalletDB(strWalletFile).WriteUnifiedFullViewingKey(ufvk);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<ZcashdUnifiedFullViewingKey> CWallet::GetUnifiedFullViewingKeyByAccount(libzcash::AccountId accountId) {
|
std::optional<ZcashdUnifiedFullViewingKey> CWallet::GetUnifiedFullViewingKeyByAccount(libzcash::AccountId accountId) const {
|
||||||
if (!mnemonicHDChain.has_value()) {
|
if (!mnemonicHDChain.has_value()) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"CWallet::GetUnifiedFullViewingKeyByAccount(): Wallet is missing mnemonic seed metadata.");
|
"CWallet::GetUnifiedFullViewingKeyByAccount(): Wallet is missing mnemonic seed metadata.");
|
||||||
|
@ -1546,6 +1546,22 @@ bool CWallet::SelectorMatchesAddress(
|
||||||
}, selector.GetPattern());
|
}, selector.GetPattern());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<RecipientAddress> CWallet::GenerateChangeAddressForAccount(
|
||||||
|
libzcash::AccountId accountId,
|
||||||
|
std::set<libzcash::ChangeType> changeOptions) {
|
||||||
|
auto ufvk = this->GetUnifiedFullViewingKeyByAccount(accountId);
|
||||||
|
if (ufvk.has_value()) {
|
||||||
|
for (libzcash::ChangeType t : changeOptions) {
|
||||||
|
if (t == libzcash::ChangeType::Transparent && accountId == ZCASH_LEGACY_ACCOUNT) {
|
||||||
|
return GenerateNewKey().GetID();
|
||||||
|
} else {
|
||||||
|
return ufvk.value().GetChangeAddress(changeOptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
SpendableInputs CWallet::FindSpendableInputs(
|
SpendableInputs CWallet::FindSpendableInputs(
|
||||||
ZTXOSelector selector,
|
ZTXOSelector selector,
|
||||||
bool allowTransparentCoinbase,
|
bool allowTransparentCoinbase,
|
||||||
|
@ -5980,8 +5996,12 @@ std::optional<libzcash::AccountId> CWallet::GetUnifiedAccountId(const libzcash::
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<UnifiedAddress> CWallet::GetUnifiedForReceiver(const Receiver& receiver) const {
|
std::optional<ZcashdUnifiedFullViewingKey> CWallet::FindUFVKByReceiver(const libzcash::Receiver& receiver) const {
|
||||||
return std::visit(LookupUnifiedAddress(*this), receiver);
|
return std::visit(UFVKForReceiver(*this), receiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<UnifiedAddress> CWallet::FindUnifiedAddressByReceiver(const Receiver& receiver) const {
|
||||||
|
return std::visit(UnifiedAddressForReceiver(*this), receiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -6213,7 +6233,6 @@ KeyAddResult AddViewingKeyToWallet::operator()(const libzcash::SaplingExtendedFu
|
||||||
return KeyNotAdded;
|
return KeyNotAdded;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyAddResult AddViewingKeyToWallet::operator()(const libzcash::UnifiedFullViewingKey& no) const {
|
KeyAddResult AddViewingKeyToWallet::operator()(const libzcash::UnifiedFullViewingKey& no) const {
|
||||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unified full viewing key import is not yet supported.");
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unified full viewing key import is not yet supported.");
|
||||||
}
|
}
|
||||||
|
@ -6271,14 +6290,53 @@ KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SaplingExtendedS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<libzcash::UnifiedAddress> LookupUnifiedAddress::operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const {
|
// UFVKForReceiver :: (CWallet&, Receiver) -> std::optional<ZcashdUnifiedFullViewingKey>
|
||||||
|
|
||||||
|
std::optional<libzcash::ZcashdUnifiedFullViewingKey> UFVKForReceiver::operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const {
|
||||||
auto ufvkPair = wallet.GetUFVKMetadataForReceiver(saplingAddr);
|
auto ufvkPair = wallet.GetUFVKMetadataForReceiver(saplingAddr);
|
||||||
if (ufvkPair.has_value()) {
|
if (ufvkPair.has_value()) {
|
||||||
auto ufvkid = ufvkPair.value().first;
|
auto ufvkid = ufvkPair.value().first;
|
||||||
auto ufvk = wallet.GetUnifiedFullViewingKey(ufvkid);
|
auto ufvk = wallet.GetUnifiedFullViewingKey(ufvkid);
|
||||||
if (!(ufvk.has_value() && ufvk.value().GetSaplingKey().has_value())) {
|
// If we have UFVK metadata, `GetUnifiedFullViewingKey` should always
|
||||||
throw std::runtime_error("CWallet::LookupUnifiedAddress(): UFVK has no Sapling key part.");
|
// return a non-nullopt value, and since we obtained that metadata by
|
||||||
}
|
// lookup from as Sapling address, it should have a Sapling key component.
|
||||||
|
assert(ufvk.has_value() && ufvk.value().GetSaplingKey().has_value());
|
||||||
|
return ufvk.value();
|
||||||
|
} else {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::optional<libzcash::ZcashdUnifiedFullViewingKey> UFVKForReceiver::operator()(const CScriptID& scriptId) const {
|
||||||
|
// We do not currently generate unified addresses containing P2SH components,
|
||||||
|
// so there's nothing to look up here.
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
std::optional<libzcash::ZcashdUnifiedFullViewingKey> UFVKForReceiver::operator()(const CKeyID& keyId) const {
|
||||||
|
auto ufvkPair = wallet.GetUFVKMetadataForReceiver(keyId);
|
||||||
|
if (ufvkPair.has_value()) {
|
||||||
|
auto ufvkid = ufvkPair.value().first;
|
||||||
|
// transparent address UFVK metadata is always accompanied by the child
|
||||||
|
// index at which the address was produced
|
||||||
|
assert(ufvkPair.value().second.has_value());
|
||||||
|
auto ufvk = wallet.GetUnifiedFullViewingKey(ufvkid);
|
||||||
|
assert(ufvk.has_value() && ufvk.value().GetTransparentKey().has_value());
|
||||||
|
return ufvk.value();
|
||||||
|
} else {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::optional<libzcash::ZcashdUnifiedFullViewingKey> UFVKForReceiver::operator()(const libzcash::UnknownReceiver& receiver) const {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnifiedAddressForReceiver :: (CWallet&, Receiver) -> std::optional<UnifiedAddress>
|
||||||
|
|
||||||
|
std::optional<libzcash::UnifiedAddress> UnifiedAddressForReceiver::operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const {
|
||||||
|
auto ufvkPair = wallet.GetUFVKMetadataForReceiver(saplingAddr);
|
||||||
|
if (ufvkPair.has_value()) {
|
||||||
|
auto ufvkid = ufvkPair.value().first;
|
||||||
|
auto ufvk = wallet.GetUnifiedFullViewingKey(ufvkid);
|
||||||
|
assert(ufvk.has_value() && ufvk.value().GetSaplingKey().has_value());
|
||||||
|
|
||||||
diversifier_index_t j;
|
diversifier_index_t j;
|
||||||
// If the wallet is missing metadata at this UFVK id, it is probably
|
// If the wallet is missing metadata at this UFVK id, it is probably
|
||||||
|
@ -6300,10 +6358,12 @@ std::optional<libzcash::UnifiedAddress> LookupUnifiedAddress::operator()(const l
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::optional<libzcash::UnifiedAddress> LookupUnifiedAddress::operator()(const CScriptID& scriptId) const {
|
std::optional<libzcash::UnifiedAddress> UnifiedAddressForReceiver::operator()(const CScriptID& scriptId) const {
|
||||||
|
// We do not currently generate unified addresses containing P2SH components,
|
||||||
|
// so there's nothing to look up here.
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
std::optional<libzcash::UnifiedAddress> LookupUnifiedAddress::operator()(const CKeyID& keyId) const {
|
std::optional<libzcash::UnifiedAddress> UnifiedAddressForReceiver::operator()(const CKeyID& keyId) const {
|
||||||
auto ufvkPair = wallet.GetUFVKMetadataForReceiver(keyId);
|
auto ufvkPair = wallet.GetUFVKMetadataForReceiver(keyId);
|
||||||
if (ufvkPair.has_value()) {
|
if (ufvkPair.has_value()) {
|
||||||
auto ufvkid = ufvkPair.value().first;
|
auto ufvkid = ufvkPair.value().first;
|
||||||
|
@ -6313,7 +6373,7 @@ std::optional<libzcash::UnifiedAddress> LookupUnifiedAddress::operator()(const C
|
||||||
diversifier_index_t j = ufvkPair.value().second.value();
|
diversifier_index_t j = ufvkPair.value().second.value();
|
||||||
auto ufvk = wallet.GetUnifiedFullViewingKey(ufvkid);
|
auto ufvk = wallet.GetUnifiedFullViewingKey(ufvkid);
|
||||||
if (!(ufvk.has_value() && ufvk.value().GetTransparentKey().has_value())) {
|
if (!(ufvk.has_value() && ufvk.value().GetTransparentKey().has_value())) {
|
||||||
throw std::runtime_error("CWallet::LookupUnifiedAddress(): UFVK has no P2PKH key part.");
|
throw std::runtime_error("CWallet::UnifiedAddressForReceiver(): UFVK has no P2PKH key part.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the wallet is missing metadata at this UFVK id, it is probably
|
// If the wallet is missing metadata at this UFVK id, it is probably
|
||||||
|
@ -6333,7 +6393,7 @@ std::optional<libzcash::UnifiedAddress> LookupUnifiedAddress::operator()(const C
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::optional<libzcash::UnifiedAddress> LookupUnifiedAddress::operator()(const libzcash::UnknownReceiver& receiver) const {
|
std::optional<libzcash::UnifiedAddress> UnifiedAddressForReceiver::operator()(const libzcash::UnknownReceiver& receiver) const {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1256,6 +1256,10 @@ public:
|
||||||
*/
|
*/
|
||||||
std::optional<libzcash::AccountId> FindAccountForSelector(const ZTXOSelector& paymentSource) const;
|
std::optional<libzcash::AccountId> FindAccountForSelector(const ZTXOSelector& paymentSource) const;
|
||||||
|
|
||||||
|
std::optional<libzcash::RecipientAddress> GenerateChangeAddressForAccount(
|
||||||
|
libzcash::AccountId accountId,
|
||||||
|
std::set<libzcash::ChangeType> changeOptions);
|
||||||
|
|
||||||
SpendableInputs FindSpendableInputs(
|
SpendableInputs FindSpendableInputs(
|
||||||
ZTXOSelector paymentSource,
|
ZTXOSelector paymentSource,
|
||||||
bool allowTransparentCoinbase,
|
bool allowTransparentCoinbase,
|
||||||
|
@ -1413,7 +1417,7 @@ public:
|
||||||
|
|
||||||
//! Retrieves the UFVK derived from the wallet's mnemonic seed for the specified account.
|
//! Retrieves the UFVK derived from the wallet's mnemonic seed for the specified account.
|
||||||
std::optional<libzcash::ZcashdUnifiedFullViewingKey>
|
std::optional<libzcash::ZcashdUnifiedFullViewingKey>
|
||||||
GetUnifiedFullViewingKeyByAccount(libzcash::AccountId account);
|
GetUnifiedFullViewingKeyByAccount(libzcash::AccountId account) const;
|
||||||
|
|
||||||
//! Generate a new unified address for the specified account, diversifier, and
|
//! Generate a new unified address for the specified account, diversifier, and
|
||||||
//! set of receiver types.
|
//! set of receiver types.
|
||||||
|
@ -1433,7 +1437,9 @@ public:
|
||||||
|
|
||||||
std::optional<libzcash::UFVKId> FindUnifiedFullViewingKey(const libzcash::UnifiedAddress& addr) const;
|
std::optional<libzcash::UFVKId> FindUnifiedFullViewingKey(const libzcash::UnifiedAddress& addr) const;
|
||||||
std::optional<libzcash::AccountId> GetUnifiedAccountId(const libzcash::UFVKId& ufvkId) const;
|
std::optional<libzcash::AccountId> GetUnifiedAccountId(const libzcash::UFVKId& ufvkId) const;
|
||||||
std::optional<libzcash::UnifiedAddress> GetUnifiedForReceiver(const libzcash::Receiver& receiver) const;
|
|
||||||
|
std::optional<libzcash::ZcashdUnifiedFullViewingKey> FindUFVKByReceiver(const libzcash::Receiver& receiver) const;
|
||||||
|
std::optional<libzcash::UnifiedAddress> FindUnifiedAddressByReceiver(const libzcash::Receiver& receiver) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Increment the next transaction order id
|
* Increment the next transaction order id
|
||||||
|
@ -1857,12 +1863,25 @@ public:
|
||||||
KeyAddResult operator()(const libzcash::SaplingExtendedSpendingKey &sk) const;
|
KeyAddResult operator()(const libzcash::SaplingExtendedSpendingKey &sk) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class LookupUnifiedAddress {
|
class UFVKForReceiver {
|
||||||
private:
|
private:
|
||||||
const CWallet& wallet;
|
const CWallet& wallet;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
LookupUnifiedAddress(const CWallet& wallet): wallet(wallet) {}
|
UFVKForReceiver(const CWallet& wallet): wallet(wallet) {}
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
class UnifiedAddressForReceiver {
|
||||||
|
private:
|
||||||
|
const CWallet& wallet;
|
||||||
|
|
||||||
|
public:
|
||||||
|
UnifiedAddressForReceiver(const CWallet& wallet): wallet(wallet) {}
|
||||||
|
|
||||||
std::optional<libzcash::UnifiedAddress> operator()(const libzcash::SaplingPaymentAddress& saplingAddr) 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 CScriptID& scriptId) const;
|
||||||
|
|
|
@ -23,39 +23,6 @@ const unsigned char ZCASH_UFVK_ID_PERSONAL[BLAKE2bPersonalBytes] =
|
||||||
|
|
||||||
namespace libzcash {
|
namespace libzcash {
|
||||||
|
|
||||||
class UnknownReceiver {
|
|
||||||
public:
|
|
||||||
uint32_t typecode;
|
|
||||||
std::vector<uint8_t> data;
|
|
||||||
|
|
||||||
UnknownReceiver(uint32_t typecode, std::vector<uint8_t> data) :
|
|
||||||
typecode(typecode), data(data) {}
|
|
||||||
|
|
||||||
friend inline bool operator==(const UnknownReceiver& a, const UnknownReceiver& b) {
|
|
||||||
return a.typecode == b.typecode && a.data == b.data;
|
|
||||||
}
|
|
||||||
friend inline bool operator<(const UnknownReceiver& a, const UnknownReceiver& b) {
|
|
||||||
// We don't know for certain the preference order of unknown receivers, but it is
|
|
||||||
// _likely_ that the higher typecode has higher preference. The exact sort order
|
|
||||||
// doesn't really matter, as unknown receivers have lower preference than known
|
|
||||||
// receivers.
|
|
||||||
return (a.typecode > b.typecode ||
|
|
||||||
(a.typecode == b.typecode && a.data < b.data));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Receivers that can appear in a Unified Address.
|
|
||||||
*
|
|
||||||
* These types are given in order of preference (as defined in ZIP 316), so that sorting
|
|
||||||
* variants by `operator<` is equivalent to sorting by preference.
|
|
||||||
*/
|
|
||||||
typedef std::variant<
|
|
||||||
SaplingPaymentAddress,
|
|
||||||
CScriptID,
|
|
||||||
CKeyID,
|
|
||||||
UnknownReceiver> Receiver;
|
|
||||||
|
|
||||||
bool HasKnownReceiverType(const Receiver& receiver);
|
bool HasKnownReceiverType(const Receiver& receiver);
|
||||||
|
|
||||||
struct ReceiverIterator {
|
struct ReceiverIterator {
|
||||||
|
@ -90,11 +57,6 @@ private:
|
||||||
size_t cur;
|
size_t cur;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** A recipient address to which a unified address can be resolved */
|
|
||||||
typedef std::variant<
|
|
||||||
CKeyID,
|
|
||||||
CScriptID,
|
|
||||||
libzcash::SaplingPaymentAddress> RecipientAddress;
|
|
||||||
|
|
||||||
class UnifiedAddress {
|
class UnifiedAddress {
|
||||||
std::vector<Receiver> receivers;
|
std::vector<Receiver> receivers;
|
||||||
|
|
|
@ -17,6 +17,7 @@ const size_t SerializedSaplingPaymentAddressSize = 43;
|
||||||
const size_t SerializedSaplingFullViewingKeySize = 96;
|
const size_t SerializedSaplingFullViewingKeySize = 96;
|
||||||
const size_t SerializedSaplingExpandedSpendingKeySize = 96;
|
const size_t SerializedSaplingExpandedSpendingKeySize = 96;
|
||||||
const size_t SerializedSaplingSpendingKeySize = 32;
|
const size_t SerializedSaplingSpendingKeySize = 32;
|
||||||
|
const size_t SerializedSaplingDiversifierKeySize = 32;
|
||||||
|
|
||||||
typedef std::array<unsigned char, ZC_DIVERSIFIER_SIZE> diversifier_t;
|
typedef std::array<unsigned char, ZC_DIVERSIFIER_SIZE> diversifier_t;
|
||||||
|
|
||||||
|
@ -133,7 +134,7 @@ public:
|
||||||
SaplingExpandedSpendingKey expanded_spending_key() const;
|
SaplingExpandedSpendingKey expanded_spending_key() const;
|
||||||
SaplingFullViewingKey full_viewing_key() const;
|
SaplingFullViewingKey full_viewing_key() const;
|
||||||
|
|
||||||
// Can derive Sapling addr from default diversifier
|
// Can derive Sapling addr from default diversifier
|
||||||
SaplingPaymentAddress default_address() const;
|
SaplingPaymentAddress default_address() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -134,3 +134,7 @@ std::optional<std::pair<UnifiedAddress, diversifier_index_t>> ZcashdUnifiedFullV
|
||||||
const diversifier_index_t& j) const {
|
const diversifier_index_t& j) const {
|
||||||
return FindAddress(j, {ReceiverType::P2PKH, ReceiverType::Sapling});
|
return FindAddress(j, {ReceiverType::P2PKH, ReceiverType::Sapling});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RecipientAddress ZcashdUnifiedFullViewingKey::GetChangeAddress(const std::set<ChangeType>& changeOptions) const {
|
||||||
|
throw std::runtime_error("TODO");
|
||||||
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
#include "bip44.h"
|
#include "bip44.h"
|
||||||
#include "key_constants.h"
|
#include "key_constants.h"
|
||||||
|
#include "script/script.h"
|
||||||
#include "zip32.h"
|
#include "zip32.h"
|
||||||
|
|
||||||
namespace libzcash {
|
namespace libzcash {
|
||||||
|
@ -18,6 +19,21 @@ enum class ReceiverType: uint32_t {
|
||||||
//Orchard = 0x03
|
//Orchard = 0x03
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** A recipient address to which a unified address can be resolved */
|
||||||
|
typedef std::variant<
|
||||||
|
CKeyID,
|
||||||
|
CScriptID,
|
||||||
|
libzcash::SaplingPaymentAddress> RecipientAddress;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An enumeration of the types of change that a transaction may
|
||||||
|
* produce.
|
||||||
|
*/
|
||||||
|
enum class ChangeType {
|
||||||
|
Sapling,
|
||||||
|
Transparent,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test whether the specified list of receiver types contains a
|
* Test whether the specified list of receiver types contains a
|
||||||
* shielded receiver type
|
* shielded receiver type
|
||||||
|
@ -32,7 +48,41 @@ bool HasTransparent(const std::set<ReceiverType>& receiverTypes);
|
||||||
|
|
||||||
class ZcashdUnifiedSpendingKey;
|
class ZcashdUnifiedSpendingKey;
|
||||||
|
|
||||||
|
class UnknownReceiver {
|
||||||
|
public:
|
||||||
|
uint32_t typecode;
|
||||||
|
std::vector<uint8_t> data;
|
||||||
|
|
||||||
|
UnknownReceiver(uint32_t typecode, std::vector<uint8_t> data) :
|
||||||
|
typecode(typecode), data(data) {}
|
||||||
|
|
||||||
|
friend inline bool operator==(const UnknownReceiver& a, const UnknownReceiver& b) {
|
||||||
|
return a.typecode == b.typecode && a.data == b.data;
|
||||||
|
}
|
||||||
|
friend inline bool operator<(const UnknownReceiver& a, const UnknownReceiver& b) {
|
||||||
|
// We don't know for certain the preference order of unknown receivers, but it is
|
||||||
|
// _likely_ that the higher typecode has higher preference. The exact sort order
|
||||||
|
// doesn't really matter, as unknown receivers have lower preference than known
|
||||||
|
// receivers.
|
||||||
|
return (a.typecode > b.typecode ||
|
||||||
|
(a.typecode == b.typecode && a.data < b.data));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receivers that can appear in a Unified Address.
|
||||||
|
*
|
||||||
|
* These types are given in order of preference (as defined in ZIP 316), so that sorting
|
||||||
|
* variants by `operator<` is equivalent to sorting by preference.
|
||||||
|
*/
|
||||||
|
typedef std::variant<
|
||||||
|
SaplingPaymentAddress,
|
||||||
|
CScriptID,
|
||||||
|
CKeyID,
|
||||||
|
UnknownReceiver> Receiver;
|
||||||
|
|
||||||
// prototypes for the classes handling ZIP-316 encoding (in Address.hpp)
|
// prototypes for the classes handling ZIP-316 encoding (in Address.hpp)
|
||||||
|
// TODO: ZIP-316 encoding should probably be moved here
|
||||||
class UnifiedAddress;
|
class UnifiedAddress;
|
||||||
class UnifiedFullViewingKey;
|
class UnifiedFullViewingKey;
|
||||||
|
|
||||||
|
@ -116,6 +166,14 @@ public:
|
||||||
*/
|
*/
|
||||||
std::optional<std::pair<UnifiedAddress, diversifier_index_t>> FindAddress(const diversifier_index_t& j) const;
|
std::optional<std::pair<UnifiedAddress, diversifier_index_t>> FindAddress(const diversifier_index_t& j) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the change address for this UFVK, given the provided
|
||||||
|
* set of receiver types for pools involved in this transaction.
|
||||||
|
* If the provided set is empty, return the change address
|
||||||
|
* corresponding to the most-preferred pool.
|
||||||
|
*/
|
||||||
|
RecipientAddress GetChangeAddress(const std::set<ChangeType>& changeOptions) const;
|
||||||
|
|
||||||
friend bool operator==(const ZcashdUnifiedFullViewingKey& a, const ZcashdUnifiedFullViewingKey& b)
|
friend bool operator==(const ZcashdUnifiedFullViewingKey& a, const ZcashdUnifiedFullViewingKey& b)
|
||||||
{
|
{
|
||||||
return a.transparentKey == b.transparentKey && a.saplingKey == b.saplingKey;
|
return a.transparentKey == b.transparentKey && a.saplingKey == b.saplingKey;
|
||||||
|
|
|
@ -126,6 +126,25 @@ libzcash::SaplingPaymentAddress SaplingDiversifiableFullViewingKey::DefaultAddre
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
libzcash::SaplingPaymentAddress SaplingDiversifiableFullViewingKey::GetChangeAddress() const {
|
||||||
|
CDataStream ss_fvk(SER_NETWORK, PROTOCOL_VERSION);
|
||||||
|
ss_fvk << fvk;
|
||||||
|
CSerializeData fvk_bytes(ss_fvk.begin(), ss_fvk.end());
|
||||||
|
|
||||||
|
SaplingDiversifiableFullViewingKey internalDFVK;
|
||||||
|
CSerializeData fvk_bytes_ret(libzcash::SerializedSaplingFullViewingKeySize);
|
||||||
|
librustzcash_zip32_sapling_derive_internal_fvk(
|
||||||
|
reinterpret_cast<unsigned char*>(fvk_bytes.data()),
|
||||||
|
dk.begin(),
|
||||||
|
reinterpret_cast<unsigned char*>(fvk_bytes_ret.data()),
|
||||||
|
internalDFVK.dk.begin());
|
||||||
|
|
||||||
|
CDataStream ss_fvk_ret(fvk_bytes_ret, SER_NETWORK, PROTOCOL_VERSION);
|
||||||
|
ss_fvk_ret >> internalDFVK.fvk;
|
||||||
|
|
||||||
|
return internalDFVK.DefaultAddress();
|
||||||
|
}
|
||||||
|
|
||||||
SaplingExtendedSpendingKey SaplingExtendedSpendingKey::Master(const HDSeed& seed)
|
SaplingExtendedSpendingKey SaplingExtendedSpendingKey::Master(const HDSeed& seed)
|
||||||
{
|
{
|
||||||
auto rawSeed = seed.RawSeed();
|
auto rawSeed = seed.RawSeed();
|
||||||
|
@ -208,6 +227,10 @@ SaplingExtendedFullViewingKey SaplingExtendedSpendingKey::ToXFVK() const
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SaplingExtendedSpendingKey SaplingExtendedSpendingKey::DeriveInternalKey() const {
|
||||||
|
throw std::runtime_error("Not yet implemented");
|
||||||
|
}
|
||||||
|
|
||||||
HDKeyPath Zip32AccountKeyPath(
|
HDKeyPath Zip32AccountKeyPath(
|
||||||
uint32_t bip44CoinType,
|
uint32_t bip44CoinType,
|
||||||
libzcash::AccountId accountId,
|
libzcash::AccountId accountId,
|
||||||
|
|
|
@ -155,6 +155,8 @@ public:
|
||||||
|
|
||||||
libzcash::SaplingPaymentAddress DefaultAddress() const;
|
libzcash::SaplingPaymentAddress DefaultAddress() const;
|
||||||
|
|
||||||
|
libzcash::SaplingPaymentAddress GetChangeAddress() const;
|
||||||
|
|
||||||
ADD_SERIALIZE_METHODS;
|
ADD_SERIALIZE_METHODS;
|
||||||
|
|
||||||
template <typename Stream, typename Operation>
|
template <typename Stream, typename Operation>
|
||||||
|
@ -237,11 +239,12 @@ struct SaplingExtendedSpendingKey {
|
||||||
static std::pair<SaplingExtendedSpendingKey, HDKeyPath> ForAccount(const HDSeed& seed, uint32_t bip44CoinType, libzcash::AccountId accountId);
|
static std::pair<SaplingExtendedSpendingKey, HDKeyPath> ForAccount(const HDSeed& seed, uint32_t bip44CoinType, libzcash::AccountId accountId);
|
||||||
static std::pair<SaplingExtendedSpendingKey, HDKeyPath> Legacy(const HDSeed& seed, uint32_t bip44CoinType, uint32_t addressIndex);
|
static std::pair<SaplingExtendedSpendingKey, HDKeyPath> Legacy(const HDSeed& seed, uint32_t bip44CoinType, uint32_t addressIndex);
|
||||||
|
|
||||||
|
|
||||||
SaplingExtendedSpendingKey Derive(uint32_t i) const;
|
SaplingExtendedSpendingKey Derive(uint32_t i) const;
|
||||||
|
|
||||||
SaplingExtendedFullViewingKey ToXFVK() const;
|
SaplingExtendedFullViewingKey ToXFVK() const;
|
||||||
|
|
||||||
|
SaplingExtendedSpendingKey DeriveInternalKey() const;
|
||||||
|
|
||||||
friend bool operator==(const SaplingExtendedSpendingKey& a, const SaplingExtendedSpendingKey& b)
|
friend bool operator==(const SaplingExtendedSpendingKey& a, const SaplingExtendedSpendingKey& b)
|
||||||
{
|
{
|
||||||
return a.depth == b.depth &&
|
return a.depth == b.depth &&
|
||||||
|
|
Loading…
Reference in New Issue