diff --git a/src/wallet/asyncrpcoperation_sendmany.cpp b/src/wallet/asyncrpcoperation_sendmany.cpp index 30b899207..e2493e161 100644 --- a/src/wallet/asyncrpcoperation_sendmany.cpp +++ b/src/wallet/asyncrpcoperation_sendmany.cpp @@ -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(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(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(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 diff --git a/src/wallet/asyncrpcoperation_sendmany.h b/src/wallet/asyncrpcoperation_sendmany.h index 7bd92dfea..cb3c4b9cb 100644 --- a/src/wallet/asyncrpcoperation_sendmany.h +++ b/src/wallet/asyncrpcoperation_sendmany.h @@ -104,7 +104,6 @@ private: bool isfromzaddr_; CTxDestination fromtaddr_; PaymentAddress frompaymentaddress_; - SpendingKey spendingkey_; Ed25519VerificationKey joinSplitPubKey_; Ed25519SigningKey joinSplitPrivKey_; diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 0ad8e24b1..faaea7cf0 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -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) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index ae0673c4a..efba55107 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -5341,43 +5341,79 @@ bool HaveSpendingKeyForPaymentAddress::operator()(const libzcash::UnifiedAddress return false; } -// GetSpendingKeyForPaymentAddress +// GetSproutKeyForPaymentAddress -std::optional GetSpendingKeyForPaymentAddress::operator()( +std::optional GetSproutKeyForPaymentAddress::operator()( const CKeyID &zaddr) const { return std::nullopt; } -std::optional GetSpendingKeyForPaymentAddress::operator()( +std::optional GetSproutKeyForPaymentAddress::operator()( const CScriptID &zaddr) const { return std::nullopt; } -std::optional GetSpendingKeyForPaymentAddress::operator()( +std::optional 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 GetSpendingKeyForPaymentAddress::operator()( +std::optional GetSproutKeyForPaymentAddress::operator()( + const libzcash::SaplingPaymentAddress &zaddr) const +{ + return std::nullopt; +} +std::optional GetSproutKeyForPaymentAddress::operator()( + const libzcash::UnifiedAddress &uaddr) const +{ + return std::nullopt; +} + +// GetSaplingKeyForPaymentAddress + +std::optional GetSaplingKeyForPaymentAddress::operator()( + const CKeyID &zaddr) const +{ + return std::nullopt; +} +std::optional GetSaplingKeyForPaymentAddress::operator()( + const CScriptID &zaddr) const +{ + return std::nullopt; +} +std::optional GetSaplingKeyForPaymentAddress::operator()( + const libzcash::SproutPaymentAddress &zaddr) const +{ + return std::nullopt; +} +std::optional 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 GetSpendingKeyForPaymentAddress::operator()( +std::optional GetSaplingKeyForPaymentAddress::operator()( const libzcash::UnifiedAddress &uaddr) const { - // TODO - return libzcash::SpendingKey(); + for (const libzcash::Receiver& receiver: uaddr) { + auto saplingAddr = std::get_if(&receiver); + if (saplingAddr != nullptr) { + libzcash::SaplingExtendedSpendingKey extsk; + if (m_wallet->GetSaplingExtendedSpendingKey(*saplingAddr, extsk)) { + return extsk; + } + } + } + return std::nullopt; } // AddViewingKeyToWallet diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index e1790dad5..bbc6ecfc2 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -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 operator()(const CKeyID &zaddr) const; - std::optional operator()(const CScriptID &zaddr) const; - std::optional operator()(const libzcash::SproutPaymentAddress &zaddr) const; - std::optional operator()(const libzcash::SaplingPaymentAddress &zaddr) const; - // FIXME: this doesn't make sense - std::optional operator()(const libzcash::UnifiedAddress &uaddr) const; + std::optional operator()(const CKeyID &zaddr) const; + std::optional operator()(const CScriptID &zaddr) const; + std::optional operator()(const libzcash::SproutPaymentAddress &zaddr) const; + std::optional operator()(const libzcash::SaplingPaymentAddress &zaddr) const; + std::optional operator()(const libzcash::UnifiedAddress &uaddr) const; +}; + +class GetSaplingKeyForPaymentAddress +{ +private: + CWallet *m_wallet; +public: + GetSaplingKeyForPaymentAddress(CWallet *wallet) : m_wallet(wallet) {} + + std::optional operator()(const CKeyID &zaddr) const; + std::optional operator()(const CScriptID &zaddr) const; + std::optional operator()(const libzcash::SproutPaymentAddress &zaddr) const; + std::optional operator()(const libzcash::SaplingPaymentAddress &zaddr) const; + std::optional operator()(const libzcash::UnifiedAddress &uaddr) const; }; enum PaymentAddressSource {