|
@ -798,6 +798,140 @@ UniValue dumpwallet_impl(const UniValue& params, bool fHelp, bool fDumpZKeys) |
|
|
return exportfilepath.string(); |
|
|
return exportfilepath.string(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
UniValue z_getalldiversifiedaddresses(const UniValue& params, bool fHelp) { |
|
|
|
|
|
if (!EnsureWalletIsAvailable(fHelp)) |
|
|
|
|
|
return NullUniValue; |
|
|
|
|
|
|
|
|
|
|
|
if (fHelp || params.size() > 1) |
|
|
|
|
|
throw runtime_error( |
|
|
|
|
|
"z_getalldiversifiedaddresses z_address\n" |
|
|
|
|
|
"\nReturns the list of all Sapling shielded addresses that share the same spending key as this address.\nThese are all peer diversified addresses." |
|
|
|
|
|
"\nArguments:\n" |
|
|
|
|
|
"1. z_address (String) The z_address to lookup\n" |
|
|
|
|
|
"\nResult:\n" |
|
|
|
|
|
"[ (json array of string)\n" |
|
|
|
|
|
" \"zaddr\" (string) a zaddr belonging to the wallet which shares the same spending key\n" |
|
|
|
|
|
" ,...\n" |
|
|
|
|
|
"]\n" |
|
|
|
|
|
"\nExamples:\n" |
|
|
|
|
|
+ HelpExampleCli("z_getalldiversifiedaddresses", "my_z_address") |
|
|
|
|
|
+ HelpExampleRpc("z_getalldiversifiedaddresses", "my_z_address") |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
LOCK2(cs_main, pwalletMain->cs_wallet); |
|
|
|
|
|
|
|
|
|
|
|
KeyIO keyIO(Params()); |
|
|
|
|
|
string strAddress = params[0].get_str(); |
|
|
|
|
|
|
|
|
|
|
|
auto in_address = keyIO.DecodePaymentAddress(strAddress); |
|
|
|
|
|
if (!IsValidPaymentAddress(in_address)) { |
|
|
|
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr"); |
|
|
|
|
|
} |
|
|
|
|
|
if (!IsValidSaplingAddress(in_address)) { |
|
|
|
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Sapling zaddr"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
UniValue ret(UniValue::VARR); |
|
|
|
|
|
|
|
|
|
|
|
// Get the incoming viewing key for the given address
|
|
|
|
|
|
libzcash::SaplingIncomingViewingKey in_ivk; |
|
|
|
|
|
libzcash::SaplingExtendedFullViewingKey in_xfvk; |
|
|
|
|
|
pwalletMain->GetSaplingIncomingViewingKey(std::get<libzcash::SaplingPaymentAddress>(in_address), in_ivk); |
|
|
|
|
|
pwalletMain->GetSaplingFullViewingKey(in_ivk, in_xfvk); |
|
|
|
|
|
|
|
|
|
|
|
std::set<libzcash::SaplingPaymentAddress> addresses; |
|
|
|
|
|
pwalletMain->GetSaplingPaymentAddresses(addresses); |
|
|
|
|
|
for (auto addr : addresses) { |
|
|
|
|
|
libzcash::SaplingIncomingViewingKey ivk; |
|
|
|
|
|
libzcash::SaplingExtendedFullViewingKey xfvk; |
|
|
|
|
|
|
|
|
|
|
|
pwalletMain->GetSaplingIncomingViewingKey(addr, ivk); |
|
|
|
|
|
pwalletMain->GetSaplingFullViewingKey(ivk, xfvk); |
|
|
|
|
|
|
|
|
|
|
|
if (ivk == in_ivk && xfvk == in_xfvk) { |
|
|
|
|
|
ret.push_back(keyIO.EncodePaymentAddress(addr)); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return ret; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
UniValue z_getnewdiversifiedaddress(const UniValue& params, bool fHelp) { |
|
|
|
|
|
if (!EnsureWalletIsAvailable(fHelp)) |
|
|
|
|
|
return NullUniValue; |
|
|
|
|
|
|
|
|
|
|
|
if (fHelp || params.size() < 1 || params.size() > 3) |
|
|
|
|
|
throw runtime_error( |
|
|
|
|
|
"z_getnewdiversifiedaddress \"z_address\"\n" |
|
|
|
|
|
"\nReturns a new diversified address based on the given z_address, and adds it to your wallet.\n" |
|
|
|
|
|
"\nArguments:\n" |
|
|
|
|
|
"1. \"z_address\" (string, required) An existing z address in the wallet.(see z_listaddresses)\n" |
|
|
|
|
|
"\nExamples:\n" |
|
|
|
|
|
"\nGet a new z address\n" |
|
|
|
|
|
+ HelpExampleCli("z_getnewdiversifiedaddress", "\"my_z_address\"") + |
|
|
|
|
|
"\nAs a JSON-RPC call\n" |
|
|
|
|
|
+ HelpExampleRpc("z_getnewdiversifiedaddress", "\"my_z_address\"") |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
LOCK2(cs_main, pwalletMain->cs_wallet); |
|
|
|
|
|
|
|
|
|
|
|
EnsureWalletIsUnlocked(); |
|
|
|
|
|
|
|
|
|
|
|
KeyIO keyIO(Params()); |
|
|
|
|
|
string strAddress = params[0].get_str(); |
|
|
|
|
|
|
|
|
|
|
|
auto in_address = keyIO.DecodePaymentAddress(strAddress); |
|
|
|
|
|
|
|
|
|
|
|
if (!IsValidPaymentAddress(in_address)) { |
|
|
|
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (!IsValidSaplingAddress(in_address)) { |
|
|
|
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Sapling zaddr"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (!std::visit(HaveSpendingKeyForPaymentAddress(pwalletMain), in_address)) { |
|
|
|
|
|
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold private zkey for this zaddr"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Get Sapling Address
|
|
|
|
|
|
auto sk = std::visit(GetSpendingKeyForPaymentAddress(pwalletMain), in_address).value(); |
|
|
|
|
|
// Now, get a new diversified address from the private key
|
|
|
|
|
|
auto espk = std::get<libzcash::SaplingExtendedSpendingKey>(sk); |
|
|
|
|
|
arith_uint88 div; |
|
|
|
|
|
|
|
|
|
|
|
libzcash::PaymentAddress address; |
|
|
|
|
|
|
|
|
|
|
|
// Iterate over the diversified addresses
|
|
|
|
|
|
while (true) { |
|
|
|
|
|
div++; |
|
|
|
|
|
|
|
|
|
|
|
// Try to obtain an address with the default diversifier
|
|
|
|
|
|
auto try_address = espk.ToXFVK().Address(ArithToUint88(div)); |
|
|
|
|
|
|
|
|
|
|
|
// If there is no address, that means the diversifier was incompatible (~50% chance)
|
|
|
|
|
|
if (!try_address.has_value()) { |
|
|
|
|
|
// Increment the diversifier and try again
|
|
|
|
|
|
continue; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Update the diversifier from the one that was returned
|
|
|
|
|
|
div = UintToArith88(try_address.value().first); |
|
|
|
|
|
|
|
|
|
|
|
// Check if the address exists
|
|
|
|
|
|
if (std::visit(PaymentAddressBelongsToWallet(pwalletMain), libzcash::PaymentAddress(try_address.value().second))) { |
|
|
|
|
|
continue; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// If it doesn't exist, then add it.
|
|
|
|
|
|
pwalletMain->AddSaplingIncomingViewingKey(espk.expsk.full_viewing_key().in_viewing_key(), try_address.value().second); |
|
|
|
|
|
address = try_address.value().second; |
|
|
|
|
|
|
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return keyIO.EncodePaymentAddress(address); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
UniValue z_importkey(const UniValue& params, bool fHelp, const CPubKey& mypk) |
|
|
UniValue z_importkey(const UniValue& params, bool fHelp, const CPubKey& mypk) |
|
|
{ |
|
|
{ |
|
@ -1064,6 +1198,147 @@ UniValue z_exportviewingkey(const UniValue& params, bool fHelp, const CPubKey& m |
|
|
return EncodeViewingKey(ivk); |
|
|
return EncodeViewingKey(ivk); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
UniValue z_exportivk(const UniValue& params, bool fHelp) |
|
|
|
|
|
{ |
|
|
|
|
|
if (!EnsureWalletIsAvailable(fHelp)) |
|
|
|
|
|
return NullUniValue; |
|
|
|
|
|
|
|
|
|
|
|
if (fHelp || params.size() != 1) |
|
|
|
|
|
throw runtime_error( |
|
|
|
|
|
"z_exportivk \"zaddr\"\n" |
|
|
|
|
|
"\nReveals the incoming viewing key corresponding to Sapling 'zaddr'.\n" |
|
|
|
|
|
"Then the z_importivk can be used with this output\n" |
|
|
|
|
|
"\nArguments:\n" |
|
|
|
|
|
"1. \"zaddr\" (string, required) The Sapling zaddr for the viewing key\n" |
|
|
|
|
|
"\nResult:\n" |
|
|
|
|
|
"\"vkey\" (string) The viewing key\n" |
|
|
|
|
|
"\nExamples:\n" |
|
|
|
|
|
+ HelpExampleCli("z_exportivk", "\"myaddress\"") |
|
|
|
|
|
+ HelpExampleRpc("z_exportivk", "\"myaddress\"") |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
LOCK2(cs_main, pwalletMain->cs_wallet); |
|
|
|
|
|
|
|
|
|
|
|
EnsureWalletIsUnlocked(); |
|
|
|
|
|
|
|
|
|
|
|
string strAddress = params[0].get_str(); |
|
|
|
|
|
|
|
|
|
|
|
KeyIO keyIO(Params()); |
|
|
|
|
|
auto address = keyIO.DecodePaymentAddress(strAddress); |
|
|
|
|
|
if (!IsValidPaymentAddress(address) || !IsValidSaplingAddress(address)) { |
|
|
|
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Sapling zaddr"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
auto spa = std::get<libzcash::SaplingPaymentAddress>(address); |
|
|
|
|
|
libzcash::SaplingIncomingViewingKey ivk; |
|
|
|
|
|
if (!pwalletMain->GetSaplingIncomingViewingKey(spa, ivk)) { |
|
|
|
|
|
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold viewing key for this zaddr"); |
|
|
|
|
|
} else { |
|
|
|
|
|
return keyIO.EncodeIVK(ivk); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
UniValue z_importivk(const UniValue& params, bool fHelp) |
|
|
|
|
|
{ |
|
|
|
|
|
if (!EnsureWalletIsAvailable(fHelp)) |
|
|
|
|
|
return NullUniValue; |
|
|
|
|
|
|
|
|
|
|
|
if (fHelp || params.size() < 1 || params.size() > 4) |
|
|
|
|
|
throw runtime_error( |
|
|
|
|
|
"z_importivk \"vkey\" ( rescan startHeight )\n" |
|
|
|
|
|
"\nAdds a viewing key (as returned by z_exportivk) to your wallet.\n" |
|
|
|
|
|
"\nArguments:\n" |
|
|
|
|
|
"1. \"vkey\" (string, required) The viewing key (see z_exportivk)\n" |
|
|
|
|
|
"2. rescan (string, optional, default=\"whenkeyisnew\") Rescan the wallet for transactions - can be \"yes\", \"no\" or \"whenkeyisnew\"\n" |
|
|
|
|
|
"3. startHeight (numeric, optional, default=0) Block height to start rescan from\n" |
|
|
|
|
|
"4. zaddr (string, optional, default=\"\") zaddr in case of importing viewing key for Sapling\n" |
|
|
|
|
|
"\nNote: This call can take minutes to complete if rescan is true.\n" |
|
|
|
|
|
"\nExamples:\n" |
|
|
|
|
|
"\nImport an incoming viewing key\n" |
|
|
|
|
|
+ HelpExampleCli("z_importivk", "\"vkey\"") + |
|
|
|
|
|
"\nImport the incoming viewing key without rescan\n" |
|
|
|
|
|
+ HelpExampleCli("z_importivk", "\"vkey\" no") + |
|
|
|
|
|
"\nImport the incoming viewing key with partial rescan\n" |
|
|
|
|
|
+ HelpExampleCli("z_importivk", "\"vkey\" whenkeyisnew 30000") + |
|
|
|
|
|
"\nRe-import the viewing key with longer partial rescan\n" |
|
|
|
|
|
+ HelpExampleCli("z_importivk", "\"vkey\" yes 20000") + |
|
|
|
|
|
"\nImport the incoming viewing key for Sapling address\n" |
|
|
|
|
|
+ HelpExampleCli("z_importivk", "\"vkey\" no 0 \"zaddr\"") + |
|
|
|
|
|
"\nAs a JSON-RPC call\n" |
|
|
|
|
|
+ HelpExampleRpc("z_importivk", "\"vkey\", \"no\"") |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
LOCK2(cs_main, pwalletMain->cs_wallet); |
|
|
|
|
|
|
|
|
|
|
|
EnsureWalletIsUnlocked(); |
|
|
|
|
|
|
|
|
|
|
|
// Whether to perform rescan after import
|
|
|
|
|
|
bool fRescan = true; |
|
|
|
|
|
bool fIgnoreExistingKey = true; |
|
|
|
|
|
if (params.size() > 1) { |
|
|
|
|
|
auto rescan = params[1].get_str(); |
|
|
|
|
|
if (rescan.compare("whenkeyisnew") != 0) { |
|
|
|
|
|
fIgnoreExistingKey = false; |
|
|
|
|
|
if (rescan.compare("no") == 0) { |
|
|
|
|
|
fRescan = false; |
|
|
|
|
|
} else if (rescan.compare("yes") != 0) { |
|
|
|
|
|
throw JSONRPCError( |
|
|
|
|
|
RPC_INVALID_PARAMETER, |
|
|
|
|
|
"rescan must be \"yes\", \"no\" or \"whenkeyisnew\""); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Height to rescan from
|
|
|
|
|
|
int nRescanHeight = 0; |
|
|
|
|
|
if (params.size() > 2) { |
|
|
|
|
|
nRescanHeight = params[2].get_int(); |
|
|
|
|
|
} |
|
|
|
|
|
if (nRescanHeight < 0 || nRescanHeight > chainActive.Height()) { |
|
|
|
|
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
string strIVKey = params[0].get_str(); |
|
|
|
|
|
KeyIO keyIO(Params()); |
|
|
|
|
|
auto ivk = keyIO.DecodeIVK(strIVKey); |
|
|
|
|
|
if (ivk.IsNull()) { |
|
|
|
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid incoming viewing key"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (params.size() < 4) { |
|
|
|
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Missing zaddr for Sapling viewing key."); |
|
|
|
|
|
} |
|
|
|
|
|
string strAddress = params[3].get_str(); |
|
|
|
|
|
auto address = keyIO.DecodePaymentAddress(strAddress); |
|
|
|
|
|
if (!IsValidSaplingAddress(address)) { |
|
|
|
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Sapling zaddr"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
auto addr = std::get<libzcash::SaplingPaymentAddress>(address); |
|
|
|
|
|
|
|
|
|
|
|
if (!(addr == ivk.address(addr.d))) { |
|
|
|
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Zaddr and viewing key are not consistent."); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (pwalletMain->HaveSaplingIncomingViewingKey(addr)) { |
|
|
|
|
|
if (fIgnoreExistingKey) { |
|
|
|
|
|
return NullUniValue; |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
pwalletMain->MarkDirty(); |
|
|
|
|
|
|
|
|
|
|
|
if (!pwalletMain->AddSaplingIncomingViewingKey(ivk, addr)) { |
|
|
|
|
|
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding viewing key to wallet"); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// We want to scan for transactions and notes
|
|
|
|
|
|
if (fRescan) { |
|
|
|
|
|
pwalletMain->ScanForWalletTransactions(chainActive[nRescanHeight], true); |
|
|
|
|
|
} |
|
|
|
|
|
return NullUniValue; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
extern int32_t HUSH_NSPV; |
|
|
extern int32_t HUSH_NSPV; |
|
|
#ifndef HUSH_NSPV_FULLNODE |
|
|
#ifndef HUSH_NSPV_FULLNODE |
|
|
#define HUSH_NSPV_FULLNODE (HUSH_NSPV <= 0) |
|
|
#define HUSH_NSPV_FULLNODE (HUSH_NSPV <= 0) |
|
|