Remove spurious variant from asyncrpcoperation_sendmany

The `spendingkey_` field previously used by asyncrpcoperation_sendmany
caused a situation where call sites would assume the type of the
spending key based upon boolean flags that were derived elsewhere.
In attempting to support unified addresses, it is desirable to remove
these boolean deductions and instead work primarily based on the
direct evidence of the data at hand. This commit therefore removes
the `spendingkey_` field that could potentially produce results
that would disagree with these boolean values, in favor of such
direct evidence, making these call sites a bit less error prone.
This commit is contained in:
Kris Nuttycombe 2021-12-20 12:25:44 -07:00
parent 890e1d841d
commit 4f2ff2a9f8
5 changed files with 128 additions and 39 deletions

View File

@ -106,7 +106,6 @@ AsyncRPCOperation_sendmany::AsyncRPCOperation_sendmany(
isfromzaddr_ = true;
frompaymentaddress_ = address.value();
spendingkey_ = std::visit(GetSpendingKeyForPaymentAddress(pwalletMain), address.value()).value();
} else {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address");
}
@ -306,9 +305,9 @@ bool AsyncRPCOperation_sendmany::main_impl() {
// Get various necessary keys
SaplingExpandedSpendingKey expsk;
uint256 ovk;
if (isfromzaddr_) {
auto sk = std::get<libzcash::SaplingExtendedSpendingKey>(spendingkey_);
expsk = sk.expsk;
auto saplingKey = std::visit(GetSaplingKeyForPaymentAddress(pwalletMain), frompaymentaddress_);
if (saplingKey.has_value()) {
expsk = saplingKey.value().expsk;
ovk = expsk.full_viewing_key().ovk;
} else {
// Sending from a t-address, which we don't have an ovk for. Instead,
@ -523,6 +522,7 @@ bool AsyncRPCOperation_sendmany::main_impl() {
UniValue obj(UniValue::VOBJ);
while (zOutputsDeque.size() > 0) {
AsyncJoinSplitInfo info;
// FIXME: make sure this .value() call is safe
info.vpub_old = 0;
info.vpub_new = 0;
int n = 0;
@ -641,7 +641,9 @@ bool AsyncRPCOperation_sendmany::main_impl() {
intermediates.insert(std::make_pair(tree.root(), tree)); // chained js are interstitial (found in between block boundaries)
// Decrypt the change note's ciphertext to retrieve some data we need
ZCNoteDecryption decryptor(std::get<libzcash::SproutSpendingKey>(spendingkey_).receiving_key());
// FIXME: make sure this .value() call is safe
auto sk = std::visit(GetSproutKeyForPaymentAddress(pwalletMain), frompaymentaddress_).value();
ZCNoteDecryption decryptor(sk.receiving_key());
auto hSig = ZCJoinSplit::h_sig(
prevJoinSplit.randomSeed,
prevJoinSplit.nullifiers,
@ -1023,7 +1025,8 @@ UniValue AsyncRPCOperation_sendmany::perform_joinsplit(
if (!witnesses[i]) {
throw runtime_error("joinsplit input could not be found in tree");
}
info.vjsin.push_back(JSInput(*witnesses[i], info.notes[i], std::get<libzcash::SproutSpendingKey>(spendingkey_)));
auto sk = std::visit(GetSproutKeyForPaymentAddress(pwalletMain), frompaymentaddress_).value();
info.vjsin.push_back(JSInput(*witnesses[i], info.notes[i], sk));
}
// Make sure there are two inputs and two outputs

View File

@ -104,7 +104,6 @@ private:
bool isfromzaddr_;
CTxDestination fromtaddr_;
PaymentAddress frompaymentaddress_;
SpendingKey spendingkey_;
Ed25519VerificationKey joinSplitPubKey_;
Ed25519SigningKey joinSplitPrivKey_;

View File

@ -3,6 +3,7 @@
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
#include "chain.h"
#include "core_io.h"
#include "key_io.h"
#include "rpc/server.h"
#include "init.h"
@ -11,6 +12,7 @@
#include "script/standard.h"
#include "sync.h"
#include "util.h"
#include "util/match.h"
#include "utiltime.h"
#include "wallet.h"
@ -67,7 +69,7 @@ std::string DecodeDumpString(const std::string &str) {
for (unsigned int pos = 0; pos < str.length(); pos++) {
unsigned char c = str[pos];
if (c == '%' && pos+2 < str.length()) {
c = (((str[pos+1]>>6)*9+((str[pos+1]-'0')&15)) << 4) |
c = (((str[pos+1]>>6)*9+((str[pos+1]-'0')&15)) << 4) |
((str[pos+2]>>6)*9+((str[pos+2]-'0')&15));
pos += 2;
}
@ -80,7 +82,7 @@ UniValue importprivkey(const UniValue& params, bool fHelp)
{
if (!EnsureWalletIsAvailable(fHelp))
return NullUniValue;
if (fHelp || params.size() < 1 || params.size() > 3)
throw runtime_error(
"importprivkey \"zcashprivkey\" ( \"label\" rescan )\n"
@ -189,7 +191,7 @@ UniValue importaddress(const UniValue& params, bool fHelp)
{
if (!EnsureWalletIsAvailable(fHelp))
return NullUniValue;
if (fHelp || params.size() < 1 || params.size() > 4)
throw runtime_error(
"importaddress \"address\" ( \"label\" rescan p2sh )\n"
@ -338,7 +340,7 @@ UniValue importwallet(const UniValue& params, bool fHelp)
{
if (!EnsureWalletIsAvailable(fHelp))
return NullUniValue;
if (fHelp || params.size() != 1)
throw runtime_error(
"importwallet \"filename\"\n"
@ -476,7 +478,7 @@ UniValue dumpprivkey(const UniValue& params, bool fHelp)
{
if (!EnsureWalletIsAvailable(fHelp))
return NullUniValue;
if (fHelp || params.size() != 1)
throw runtime_error(
"dumpprivkey \"t-addr\"\n"
@ -520,7 +522,7 @@ UniValue z_exportwallet(const UniValue& params, bool fHelp)
{
if (!EnsureWalletIsAvailable(fHelp))
return NullUniValue;
if (fHelp || params.size() != 1)
throw runtime_error(
"z_exportwallet \"filename\"\n"
@ -769,10 +771,10 @@ UniValue z_importkey(const UniValue& params, bool fHelp)
if (addResult == KeyNotAdded) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding spending key to wallet");
}
// whenever a key is imported, we need to scan the whole chain
pwalletMain->nTimeFirstKey = 1; // 0 would be considered 'no value'
// We want to scan for transactions and notes
if (fRescan) {
pwalletMain->ScanForWalletTransactions(chainActive[nRescanHeight], true);
@ -909,12 +911,48 @@ UniValue z_exportkey(const UniValue& params, bool fHelp)
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr");
}
// Sapling support
auto sk = std::visit(GetSpendingKeyForPaymentAddress(pwalletMain), address.value());
if (!sk) {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold private zkey for this zaddr");
}
return keyIO.EncodeSpendingKey(sk.value());
std::string result = std::visit(match {
[&](const CKeyID& addr) {
CKey key;
if (pwalletMain->GetKey(addr, key)) {
return keyIO.EncodeSecret(key);
} else {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold the private key for this address.");
}
},
[&](const CScriptID& addr) {
CScript redeemScript;
if (pwalletMain->GetCScript(addr, redeemScript)) {
return FormatScript(redeemScript);
} else {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold the private key for this address.");
}
},
[&](const libzcash::SproutPaymentAddress& addr) {
libzcash::SproutSpendingKey key;
if (pwalletMain->GetSproutSpendingKey(addr, key)) {
return keyIO.EncodeSpendingKey(key);
} else {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold the private zkey for this zaddr");
}
},
[&](const libzcash::SaplingPaymentAddress& addr) {
libzcash::SaplingExtendedSpendingKey extsk;
if (pwalletMain->GetSaplingExtendedSpendingKey(addr, extsk)) {
return keyIO.EncodeSpendingKey(extsk);
} else {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold the private zkey for this zaddr");
}
},
[&](const libzcash::UnifiedAddress& ua) {
throw JSONRPCError(
RPC_WALLET_ERROR,
"No serialized form is defined for unified spending keys. "
"Use the emergency recovery phrase for this wallet for backup purposes instead.");
return std::string(); //unreachable, here to make the compiler happy
}
}, address.value());
return result;
}
UniValue z_exportviewingkey(const UniValue& params, bool fHelp)

View File

@ -5341,43 +5341,79 @@ bool HaveSpendingKeyForPaymentAddress::operator()(const libzcash::UnifiedAddress
return false;
}
// GetSpendingKeyForPaymentAddress
// GetSproutKeyForPaymentAddress
std::optional<libzcash::SpendingKey> GetSpendingKeyForPaymentAddress::operator()(
std::optional<libzcash::SproutSpendingKey> GetSproutKeyForPaymentAddress::operator()(
const CKeyID &zaddr) const
{
return std::nullopt;
}
std::optional<libzcash::SpendingKey> GetSpendingKeyForPaymentAddress::operator()(
std::optional<libzcash::SproutSpendingKey> GetSproutKeyForPaymentAddress::operator()(
const CScriptID &zaddr) const
{
return std::nullopt;
}
std::optional<libzcash::SpendingKey> GetSpendingKeyForPaymentAddress::operator()(
std::optional<libzcash::SproutSpendingKey> GetSproutKeyForPaymentAddress::operator()(
const libzcash::SproutPaymentAddress &zaddr) const
{
libzcash::SproutSpendingKey k;
if (m_wallet->GetSproutSpendingKey(zaddr, k)) {
return libzcash::SpendingKey(k);
return k;
} else {
return std::nullopt;
}
}
std::optional<libzcash::SpendingKey> GetSpendingKeyForPaymentAddress::operator()(
std::optional<libzcash::SproutSpendingKey> GetSproutKeyForPaymentAddress::operator()(
const libzcash::SaplingPaymentAddress &zaddr) const
{
return std::nullopt;
}
std::optional<libzcash::SproutSpendingKey> GetSproutKeyForPaymentAddress::operator()(
const libzcash::UnifiedAddress &uaddr) const
{
return std::nullopt;
}
// GetSaplingKeyForPaymentAddress
std::optional<libzcash::SaplingExtendedSpendingKey> GetSaplingKeyForPaymentAddress::operator()(
const CKeyID &zaddr) const
{
return std::nullopt;
}
std::optional<libzcash::SaplingExtendedSpendingKey> GetSaplingKeyForPaymentAddress::operator()(
const CScriptID &zaddr) const
{
return std::nullopt;
}
std::optional<libzcash::SaplingExtendedSpendingKey> GetSaplingKeyForPaymentAddress::operator()(
const libzcash::SproutPaymentAddress &zaddr) const
{
return std::nullopt;
}
std::optional<libzcash::SaplingExtendedSpendingKey> GetSaplingKeyForPaymentAddress::operator()(
const libzcash::SaplingPaymentAddress &zaddr) const
{
libzcash::SaplingExtendedSpendingKey extsk;
if (m_wallet->GetSaplingExtendedSpendingKey(zaddr, extsk)) {
return libzcash::SpendingKey(extsk);
return extsk;
} else {
return std::nullopt;
}
}
std::optional<libzcash::SpendingKey> GetSpendingKeyForPaymentAddress::operator()(
std::optional<libzcash::SaplingExtendedSpendingKey> GetSaplingKeyForPaymentAddress::operator()(
const libzcash::UnifiedAddress &uaddr) const
{
// TODO
return libzcash::SpendingKey();
for (const libzcash::Receiver& receiver: uaddr) {
auto saplingAddr = std::get_if<SaplingPaymentAddress>(&receiver);
if (saplingAddr != nullptr) {
libzcash::SaplingExtendedSpendingKey extsk;
if (m_wallet->GetSaplingExtendedSpendingKey(*saplingAddr, extsk)) {
return extsk;
}
}
}
return std::nullopt;
}
// AddViewingKeyToWallet

View File

@ -1404,19 +1404,32 @@ public:
bool operator()(const libzcash::UnifiedAddress &uaddr) const;
};
class GetSpendingKeyForPaymentAddress
class GetSproutKeyForPaymentAddress
{
private:
CWallet *m_wallet;
public:
GetSpendingKeyForPaymentAddress(CWallet *wallet) : m_wallet(wallet) {}
GetSproutKeyForPaymentAddress(CWallet *wallet) : m_wallet(wallet) {}
std::optional<libzcash::SpendingKey> operator()(const CKeyID &zaddr) const;
std::optional<libzcash::SpendingKey> operator()(const CScriptID &zaddr) const;
std::optional<libzcash::SpendingKey> operator()(const libzcash::SproutPaymentAddress &zaddr) const;
std::optional<libzcash::SpendingKey> operator()(const libzcash::SaplingPaymentAddress &zaddr) const;
// FIXME: this doesn't make sense
std::optional<libzcash::SpendingKey> operator()(const libzcash::UnifiedAddress &uaddr) const;
std::optional<libzcash::SproutSpendingKey> operator()(const CKeyID &zaddr) const;
std::optional<libzcash::SproutSpendingKey> operator()(const CScriptID &zaddr) const;
std::optional<libzcash::SproutSpendingKey> operator()(const libzcash::SproutPaymentAddress &zaddr) const;
std::optional<libzcash::SproutSpendingKey> operator()(const libzcash::SaplingPaymentAddress &zaddr) const;
std::optional<libzcash::SproutSpendingKey> operator()(const libzcash::UnifiedAddress &uaddr) const;
};
class GetSaplingKeyForPaymentAddress
{
private:
CWallet *m_wallet;
public:
GetSaplingKeyForPaymentAddress(CWallet *wallet) : m_wallet(wallet) {}
std::optional<libzcash::SaplingExtendedSpendingKey> operator()(const CKeyID &zaddr) const;
std::optional<libzcash::SaplingExtendedSpendingKey> operator()(const CScriptID &zaddr) const;
std::optional<libzcash::SaplingExtendedSpendingKey> operator()(const libzcash::SproutPaymentAddress &zaddr) const;
std::optional<libzcash::SaplingExtendedSpendingKey> operator()(const libzcash::SaplingPaymentAddress &zaddr) const;
std::optional<libzcash::SaplingExtendedSpendingKey> operator()(const libzcash::UnifiedAddress &uaddr) const;
};
enum PaymentAddressSource {