Auto merge of #2143 - str4d:1997-viewing-keys, r=str4d

Implement incoming viewing keys

Closes #1997.
This commit is contained in:
Homu
2017-12-20 14:11:05 -08:00
28 changed files with 698 additions and 123 deletions

View File

@@ -555,7 +555,7 @@ 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(spendingkey_.viewing_key());
ZCNoteDecryption decryptor(spendingkey_.receiving_key());
auto hSig = prevJoinSplit.h_sig(*pzcashParams, tx_.joinSplitPubKey);
try {
NotePlaintext plaintext = NotePlaintext::decrypt(

View File

@@ -316,14 +316,14 @@ bool CCryptoKeyStore::AddSpendingKey(const libzcash::SpendingKey &sk)
if (!EncryptSecret(vMasterKey, vchSecret, address.GetHash(), vchCryptedSecret))
return false;
if (!AddCryptedSpendingKey(address, sk.viewing_key(), vchCryptedSecret))
if (!AddCryptedSpendingKey(address, sk.receiving_key(), vchCryptedSecret))
return false;
}
return true;
}
bool CCryptoKeyStore::AddCryptedSpendingKey(const libzcash::PaymentAddress &address,
const libzcash::ViewingKey &vk,
const libzcash::ReceivingKey &rk,
const std::vector<unsigned char> &vchCryptedSecret)
{
{
@@ -332,7 +332,7 @@ bool CCryptoKeyStore::AddCryptedSpendingKey(const libzcash::PaymentAddress &addr
return false;
mapCryptedSpendingKeys[address] = vchCryptedSecret;
mapNoteDecryptors.insert(std::make_pair(address, ZCNoteDecryption(vk)));
mapNoteDecryptors.insert(std::make_pair(address, ZCNoteDecryption(rk)));
}
return true;
}
@@ -384,7 +384,7 @@ bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial& vMasterKeyIn)
std::vector<unsigned char> vchCryptedSecret;
if (!EncryptSecret(vMasterKeyIn, vchSecret, address.GetHash(), vchCryptedSecret))
return false;
if (!AddCryptedSpendingKey(address, sk.viewing_key(), vchCryptedSecret))
if (!AddCryptedSpendingKey(address, sk.receiving_key(), vchCryptedSecret))
return false;
}
mapSpendingKeys.clear();

View File

@@ -201,7 +201,7 @@ public:
}
}
virtual bool AddCryptedSpendingKey(const libzcash::PaymentAddress &address,
const libzcash::ViewingKey &vk,
const libzcash::ReceivingKey &rk,
const std::vector<unsigned char> &vchCryptedSecret);
bool AddSpendingKey(const libzcash::SpendingKey &sk);
bool HaveSpendingKey(const libzcash::PaymentAddress &address) const

View File

@@ -328,7 +328,7 @@ TEST(wallet_tests, GetNoteNullifier) {
auto sk = libzcash::SpendingKey::random();
auto address = sk.address();
auto dec = ZCNoteDecryption(sk.viewing_key());
auto dec = ZCNoteDecryption(sk.receiving_key());
auto wtx = GetValidReceive(sk, 10, true);
auto note = GetNote(sk, wtx, 0, 1);

View File

@@ -66,6 +66,53 @@ TEST(wallet_zkeys_tests, store_and_load_zkeys) {
ASSERT_EQ(m.nCreateTime, now);
}
/**
* This test covers methods on CWallet
* AddViewingKey()
* RemoveViewingKey()
* LoadViewingKey()
*/
TEST(wallet_zkeys_tests, StoreAndLoadViewingKeys) {
SelectParams(CBaseChainParams::MAIN);
CWallet wallet;
// wallet should be empty
std::set<libzcash::PaymentAddress> addrs;
wallet.GetPaymentAddresses(addrs);
ASSERT_EQ(0, addrs.size());
// manually add new viewing key to wallet
auto sk = libzcash::SpendingKey::random();
auto vk = sk.viewing_key();
ASSERT_TRUE(wallet.AddViewingKey(vk));
// verify wallet did add it
auto addr = sk.address();
ASSERT_TRUE(wallet.HaveViewingKey(addr));
// and that we don't have the corresponding spending key
ASSERT_FALSE(wallet.HaveSpendingKey(addr));
// verify viewing key stored correctly
libzcash::ViewingKey vkOut;
wallet.GetViewingKey(addr, vkOut);
ASSERT_EQ(vk, vkOut);
// Load a second viewing key into the wallet
auto sk2 = libzcash::SpendingKey::random();
ASSERT_TRUE(wallet.LoadViewingKey(sk2.viewing_key()));
// verify wallet did add it
auto addr2 = sk2.address();
ASSERT_TRUE(wallet.HaveViewingKey(addr2));
ASSERT_FALSE(wallet.HaveSpendingKey(addr2));
// Remove the first viewing key
ASSERT_TRUE(wallet.RemoveViewingKey(vk));
ASSERT_FALSE(wallet.HaveViewingKey(addr));
ASSERT_TRUE(wallet.HaveViewingKey(addr2));
}
/**
* This test covers methods on CWalletDB
* WriteZKey()
@@ -138,6 +185,50 @@ TEST(wallet_zkeys_tests, write_zkey_direct_to_db) {
ASSERT_EQ(m.nCreateTime, now);
}
/**
* This test covers methods on CWalletDB
* WriteViewingKey()
*/
TEST(wallet_zkeys_tests, WriteViewingKeyDirectToDB) {
SelectParams(CBaseChainParams::TESTNET);
// Get temporary and unique path for file.
// Note: / operator to append paths
boost::filesystem::path pathTemp = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
boost::filesystem::create_directories(pathTemp);
mapArgs["-datadir"] = pathTemp.string();
bool fFirstRun;
CWallet wallet("wallet-vkey.dat");
ASSERT_EQ(DB_LOAD_OK, wallet.LoadWallet(fFirstRun));
// No default CPubKey set
ASSERT_TRUE(fFirstRun);
// create random viewing key and add it to database directly, bypassing wallet
auto sk = libzcash::SpendingKey::random();
auto vk = sk.viewing_key();
auto addr = sk.address();
int64_t now = GetTime();
CKeyMetadata meta(now);
CWalletDB db("wallet-vkey.dat");
db.WriteViewingKey(vk);
// wallet should not be aware of viewing key
ASSERT_FALSE(wallet.HaveViewingKey(addr));
// load the wallet again
ASSERT_EQ(DB_LOAD_OK, wallet.LoadWallet(fFirstRun));
// wallet can now see the viewing key
ASSERT_TRUE(wallet.HaveViewingKey(addr));
// check key is the same
libzcash::ViewingKey vkOut;
wallet.GetViewingKey(addr, vkOut);
ASSERT_EQ(vk, vkOut);
}
/**

View File

@@ -648,6 +648,94 @@ UniValue z_importkey(const UniValue& params, bool fHelp)
return NullUniValue;
}
UniValue z_importviewingkey(const UniValue& params, bool fHelp)
{
if (!EnsureWalletIsAvailable(fHelp))
return NullUniValue;
if (fHelp || params.size() < 1 || params.size() > 2)
throw runtime_error(
"z_importviewingkey \"vkey\" ( rescan startHeight )\n"
"\nAdds a viewing key (as returned by z_exportviewingkey) to your wallet.\n"
"\nArguments:\n"
"1. \"vkey\" (string, required) The viewing key (see z_exportviewingkey)\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"
"\nNote: This call can take minutes to complete if rescan is true.\n"
"\nExamples:\n"
"\nImport a viewing key\n"
+ HelpExampleCli("z_importviewingkey", "\"vkey\"") +
"\nImport the viewing key without rescan\n"
+ HelpExampleCli("z_importviewingkey", "\"vkey\", no") +
"\nImport the viewing key with partial rescan\n"
+ HelpExampleCli("z_importviewingkey", "\"vkey\" whenkeyisnew 30000") +
"\nRe-import the viewing key with longer partial rescan\n"
+ HelpExampleCli("z_importviewingkey", "\"vkey\" yes 20000") +
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("z_importviewingkey", "\"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 strVKey = params[0].get_str();
CZCViewingKey viewingkey(strVKey);
auto vkey = viewingkey.Get();
auto addr = vkey.address();
{
if (pwalletMain->HaveSpendingKey(addr)) {
throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this viewing key");
}
// Don't throw error in case a viewing key is already there
if (pwalletMain->HaveViewingKey(addr)) {
if (fIgnoreExistingKey) {
return NullUniValue;
}
} else {
pwalletMain->MarkDirty();
if (!pwalletMain->AddViewingKey(vkey)) {
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;
}
UniValue z_exportkey(const UniValue& params, bool fHelp)
{
@@ -686,3 +774,43 @@ UniValue z_exportkey(const UniValue& params, bool fHelp)
return spendingkey.ToString();
}
UniValue z_exportviewingkey(const UniValue& params, bool fHelp)
{
if (!EnsureWalletIsAvailable(fHelp))
return NullUniValue;
if (fHelp || params.size() != 1)
throw runtime_error(
"z_exportviewingkey \"zaddr\"\n"
"\nReveals the viewing key corresponding to 'zaddr'.\n"
"Then the z_importviewingkey can be used with this output\n"
"\nArguments:\n"
"1. \"zaddr\" (string, required) The zaddr for the viewing key\n"
"\nResult:\n"
"\"vkey\" (string) The viewing key\n"
"\nExamples:\n"
+ HelpExampleCli("z_exportviewingkey", "\"myaddress\"")
+ HelpExampleRpc("z_exportviewingkey", "\"myaddress\"")
);
LOCK2(cs_main, pwalletMain->cs_wallet);
EnsureWalletIsUnlocked();
string strAddress = params[0].get_str();
CZCPaymentAddress address(strAddress);
auto addr = address.Get();
libzcash::ViewingKey vk;
if (!pwalletMain->GetViewingKey(addr, vk)) {
libzcash::SpendingKey k;
if (!pwalletMain->GetSpendingKey(addr, k)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold private key or viewing key for this zaddr");
}
vk = k.viewing_key();
}
CZCViewingKey viewingkey(vk);
return viewingkey.ToString();
}

View File

@@ -2667,7 +2667,7 @@ UniValue zc_raw_receive(const UniValue& params, bool fHelp)
}
}
ZCNoteDecryption decryptor(k.viewing_key());
ZCNoteDecryption decryptor(k.receiving_key());
NotePlaintext npt = NotePlaintext::decrypt(
decryptor,
@@ -2902,6 +2902,7 @@ UniValue zc_raw_keygen(const UniValue& params, bool fHelp)
"Output: {\n"
" \"zcaddress\": zcaddr,\n"
" \"zcsecretkey\": zcsecretkey,\n"
" \"zcviewingkey\": zcviewingkey,\n"
"}\n"
);
}
@@ -2910,18 +2911,14 @@ UniValue zc_raw_keygen(const UniValue& params, bool fHelp)
auto addr = k.address();
auto viewing_key = k.viewing_key();
CDataStream viewing(SER_NETWORK, PROTOCOL_VERSION);
viewing << viewing_key;
CZCPaymentAddress pubaddr(addr);
CZCSpendingKey spendingkey(k);
std::string viewing_hex = HexStr(viewing.begin(), viewing.end());
CZCViewingKey viewingkey(viewing_key);
UniValue result(UniValue::VOBJ);
result.push_back(Pair("zcaddress", pubaddr.ToString()));
result.push_back(Pair("zcsecretkey", spendingkey.ToString()));
result.push_back(Pair("zcviewingkey", viewing_hex));
result.push_back(Pair("zcviewingkey", viewingkey.ToString()));
return result;
}
@@ -2960,9 +2957,10 @@ UniValue z_listaddresses(const UniValue& params, bool fHelp)
if (fHelp || params.size() > 1)
throw runtime_error(
"z_listaddresses\n"
"z_listaddresses ( includeWatchonly )\n"
"\nReturns the list of zaddr belonging to the wallet.\n"
"\nArguments:\n"
"1. includeWatchonly (bool, optional, default=false) Also include watchonly addresses (see 'z_importviewingkey')\n"
"\nResult:\n"
"[ (json array of string)\n"
" \"zaddr\" (string) a zaddr belonging to the wallet\n"
@@ -2975,16 +2973,23 @@ UniValue z_listaddresses(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet);
bool fIncludeWatchonly = false;
if (params.size() > 0) {
fIncludeWatchonly = params[0].get_bool();
}
UniValue ret(UniValue::VARR);
std::set<libzcash::PaymentAddress> addresses;
pwalletMain->GetPaymentAddresses(addresses);
for (auto addr : addresses ) {
ret.push_back(CZCPaymentAddress(addr).ToString());
if (fIncludeWatchonly || pwalletMain->HaveSpendingKey(addr)) {
ret.push_back(CZCPaymentAddress(addr).ToString());
}
}
return ret;
}
CAmount getBalanceTaddr(std::string transparentAddress, int minDepth=1) {
CAmount getBalanceTaddr(std::string transparentAddress, int minDepth=1, bool ignoreUnspendable=true) {
set<CBitcoinAddress> setAddress;
vector<COutput> vecOutputs;
CAmount balance = 0;
@@ -3006,6 +3011,10 @@ CAmount getBalanceTaddr(std::string transparentAddress, int minDepth=1) {
continue;
}
if (ignoreUnspendable && !out.fSpendable) {
continue;
}
if (setAddress.size()) {
CTxDestination address;
if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) {
@@ -3023,11 +3032,11 @@ CAmount getBalanceTaddr(std::string transparentAddress, int minDepth=1) {
return balance;
}
CAmount getBalanceZaddr(std::string address, int minDepth = 1) {
CAmount getBalanceZaddr(std::string address, int minDepth = 1, bool ignoreUnspendable=true) {
CAmount balance = 0;
std::vector<CNotePlaintextEntry> entries;
LOCK2(cs_main, pwalletMain->cs_wallet);
pwalletMain->GetFilteredNotes(entries, address, minDepth);
pwalletMain->GetFilteredNotes(entries, address, minDepth, true, ignoreUnspendable);
for (auto & entry : entries) {
balance += CAmount(entry.plaintext.value);
}
@@ -3079,14 +3088,14 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp)
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr.");
}
if (!pwalletMain->HaveSpendingKey(zaddr)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, zaddr spending key not found.");
if (!(pwalletMain->HaveSpendingKey(zaddr) || pwalletMain->HaveViewingKey(zaddr))) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, zaddr spending key or viewing key not found.");
}
UniValue result(UniValue::VARR);
std::vector<CNotePlaintextEntry> entries;
pwalletMain->GetFilteredNotes(entries, fromaddress, nMinDepth, false);
pwalletMain->GetFilteredNotes(entries, fromaddress, nMinDepth, false, false);
for (CNotePlaintextEntry & entry : entries) {
UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("txid",entry.jsop.hash.ToString()));
@@ -3108,6 +3117,8 @@ UniValue z_getbalance(const UniValue& params, bool fHelp)
throw runtime_error(
"z_getbalance \"address\" ( minconf )\n"
"\nReturns the balance of a taddr or zaddr belonging to the nodes wallet.\n"
"\nCAUTION: If address is a watch-only zaddr, the returned balance may be larger than the actual balance,"
"\nbecause spends cannot be detected with incoming viewing keys.\n"
"\nArguments:\n"
"1. \"address\" (string) The selected address. It may be a transparent or private address.\n"
"2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n"
@@ -3145,16 +3156,16 @@ UniValue z_getbalance(const UniValue& params, bool fHelp)
} catch (const std::runtime_error&) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address, should be a taddr or zaddr.");
}
if (!pwalletMain->HaveSpendingKey(zaddr)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, zaddr spending key not found.");
if (!(pwalletMain->HaveSpendingKey(zaddr) || pwalletMain->HaveViewingKey(zaddr))) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, zaddr spending key or viewing key not found.");
}
}
CAmount nBalance = 0;
if (fromTaddr) {
nBalance = getBalanceTaddr(fromaddress, nMinDepth);
nBalance = getBalanceTaddr(fromaddress, nMinDepth, false);
} else {
nBalance = getBalanceZaddr(fromaddress, nMinDepth);
nBalance = getBalanceZaddr(fromaddress, nMinDepth, false);
}
return ValueFromAmount(nBalance);
@@ -3166,12 +3177,15 @@ UniValue z_gettotalbalance(const UniValue& params, bool fHelp)
if (!EnsureWalletIsAvailable(fHelp))
return NullUniValue;
if (fHelp || params.size() > 1)
if (fHelp || params.size() > 2)
throw runtime_error(
"z_gettotalbalance ( minconf )\n"
"z_gettotalbalance ( minconf includeWatchonly )\n"
"\nReturn the total value of funds stored in the nodes wallet.\n"
"\nCAUTION: If the wallet contains watch-only zaddrs, the returned private balance may be larger than the actual balance,"
"\nbecause spends cannot be detected with incoming viewing keys.\n"
"\nArguments:\n"
"1. minconf (numeric, optional, default=1) Only include private and transparent transactions confirmed at least this many times.\n"
"2. includeWatchonly (bool, optional, default=false) Also include balance in watchonly addresses (see 'importaddress' and 'z_importviewingkey')\n"
"\nResult:\n"
"{\n"
" \"transparent\": xxxxx, (numeric) the total balance of transparent funds\n"
@@ -3190,19 +3204,24 @@ UniValue z_gettotalbalance(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet);
int nMinDepth = 1;
if (params.size() == 1) {
if (params.size() > 0) {
nMinDepth = params[0].get_int();
}
if (nMinDepth < 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0");
}
bool fIncludeWatchonly = false;
if (params.size() > 1) {
fIncludeWatchonly = params[1].get_bool();
}
// getbalance and "getbalance * 1 true" should return the same number
// but they don't because wtx.GetAmounts() does not handle tx where there are no outputs
// pwalletMain->GetBalance() does not accept min depth parameter
// so we use our own method to get balance of utxos.
CAmount nBalance = getBalanceTaddr("", nMinDepth);
CAmount nPrivateBalance = getBalanceZaddr("", nMinDepth);
CAmount nBalance = getBalanceTaddr("", nMinDepth, !fIncludeWatchonly);
CAmount nPrivateBalance = getBalanceZaddr("", nMinDepth, !fIncludeWatchonly);
CAmount nTotalBalance = nBalance + nPrivateBalance;
UniValue result(UniValue::VOBJ);
result.push_back(Pair("transparent", FormatMoney(nBalance)));

View File

@@ -107,6 +107,10 @@ bool CWallet::AddZKey(const libzcash::SpendingKey &key)
if (!CCryptoKeyStore::AddSpendingKey(key))
return false;
// check if we need to remove from viewing keys
if (HaveViewingKey(addr))
RemoveViewingKey(key.viewing_key());
if (!fFileBacked)
return true;
@@ -190,10 +194,10 @@ bool CWallet::AddCryptedKey(const CPubKey &vchPubKey,
bool CWallet::AddCryptedSpendingKey(const libzcash::PaymentAddress &address,
const libzcash::ViewingKey &vk,
const libzcash::ReceivingKey &rk,
const std::vector<unsigned char> &vchCryptedSecret)
{
if (!CCryptoKeyStore::AddCryptedSpendingKey(address, vk, vchCryptedSecret))
if (!CCryptoKeyStore::AddCryptedSpendingKey(address, rk, vchCryptedSecret))
return false;
if (!fFileBacked)
return true;
@@ -201,12 +205,12 @@ bool CWallet::AddCryptedSpendingKey(const libzcash::PaymentAddress &address,
LOCK(cs_wallet);
if (pwalletdbEncryption) {
return pwalletdbEncryption->WriteCryptedZKey(address,
vk,
rk,
vchCryptedSecret,
mapZKeyMetadata[address]);
} else {
return CWalletDB(strWalletFile).WriteCryptedZKey(address,
vk,
rk,
vchCryptedSecret,
mapZKeyMetadata[address]);
}
@@ -236,9 +240,9 @@ bool CWallet::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigne
return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret);
}
bool CWallet::LoadCryptedZKey(const libzcash::PaymentAddress &addr, const libzcash::ViewingKey &vk, const std::vector<unsigned char> &vchCryptedSecret)
bool CWallet::LoadCryptedZKey(const libzcash::PaymentAddress &addr, const libzcash::ReceivingKey &rk, const std::vector<unsigned char> &vchCryptedSecret)
{
return CCryptoKeyStore::AddCryptedSpendingKey(addr, vk, vchCryptedSecret);
return CCryptoKeyStore::AddCryptedSpendingKey(addr, rk, vchCryptedSecret);
}
bool CWallet::LoadZKey(const libzcash::SpendingKey &key)
@@ -246,6 +250,38 @@ bool CWallet::LoadZKey(const libzcash::SpendingKey &key)
return CCryptoKeyStore::AddSpendingKey(key);
}
bool CWallet::AddViewingKey(const libzcash::ViewingKey &vk)
{
if (!CCryptoKeyStore::AddViewingKey(vk)) {
return false;
}
nTimeFirstKey = 1; // No birthday information for viewing keys.
if (!fFileBacked) {
return true;
}
return CWalletDB(strWalletFile).WriteViewingKey(vk);
}
bool CWallet::RemoveViewingKey(const libzcash::ViewingKey &vk)
{
AssertLockHeld(cs_wallet);
if (!CCryptoKeyStore::RemoveViewingKey(vk)) {
return false;
}
if (fFileBacked) {
if (!CWalletDB(strWalletFile).EraseViewingKey(vk)) {
return false;
}
}
return true;
}
bool CWallet::LoadViewingKey(const libzcash::ViewingKey &vk)
{
return CCryptoKeyStore::AddViewingKey(vk);
}
bool CWallet::AddCScript(const CScript& redeemScript)
{
if (!CCryptoKeyStore::AddCScript(redeemScript))
@@ -946,7 +982,8 @@ void CWallet::MarkDirty()
}
/**
* Ensure that every note in the wallet has a cached nullifier.
* Ensure that every note in the wallet (for which we possess a spending key)
* has a cached nullifier.
*/
bool CWallet::UpdateNullifierNoteMap()
{
@@ -960,16 +997,17 @@ bool CWallet::UpdateNullifierNoteMap()
for (std::pair<const uint256, CWalletTx>& wtxItem : mapWallet) {
for (mapNoteData_t::value_type& item : wtxItem.second.mapNoteData) {
if (!item.second.nullifier) {
auto i = item.first.js;
GetNoteDecryptor(item.second.address, dec);
auto hSig = wtxItem.second.vjoinsplit[i].h_sig(
*pzcashParams, wtxItem.second.joinSplitPubKey);
item.second.nullifier = GetNoteNullifier(
wtxItem.second.vjoinsplit[i],
item.second.address,
dec,
hSig,
item.first.n);
if (GetNoteDecryptor(item.second.address, dec)) {
auto i = item.first.js;
auto hSig = wtxItem.second.vjoinsplit[i].h_sig(
*pzcashParams, wtxItem.second.joinSplitPubKey);
item.second.nullifier = GetNoteNullifier(
wtxItem.second.vjoinsplit[i],
item.second.address,
dec,
hSig,
item.first.n);
}
}
}
UpdateNullifierNoteMapWithTx(wtxItem.second);
@@ -1231,7 +1269,9 @@ boost::optional<uint256> CWallet::GetNoteNullifier(const JSDescription& jsdesc,
hSig,
(unsigned char) n);
auto note = note_pt.note(address);
// SpendingKeys are only available if the wallet is unlocked
// SpendingKeys are only available if:
// - We have them (this isn't a viewing key)
// - The wallet is unlocked
libzcash::SpendingKey key;
if (GetSpendingKey(address, key)) {
ret = note.nullifier(key);
@@ -3608,7 +3648,7 @@ bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, bool fRejectAbsurdFee)
* Find notes in the wallet filtered by payment address, min depth and ability to spend.
* These notes are decrypted and added to the output parameter vector, outEntries.
*/
void CWallet::GetFilteredNotes(std::vector<CNotePlaintextEntry> & outEntries, std::string address, int minDepth, bool ignoreSpent)
void CWallet::GetFilteredNotes(std::vector<CNotePlaintextEntry> & outEntries, std::string address, int minDepth, bool ignoreSpent, bool ignoreUnspendable)
{
bool fFilterAddress = false;
libzcash::PaymentAddress filterPaymentAddress;
@@ -3646,6 +3686,11 @@ void CWallet::GetFilteredNotes(std::vector<CNotePlaintextEntry> & outEntries, st
continue;
}
// skip notes which cannot be spent
if (ignoreUnspendable && !HaveSpendingKey(pa)) {
continue;
}
int i = jsop.js; // Index into CTransaction.vjoinsplit
int j = jsop.n; // Index into JSDescription.ciphertexts

View File

@@ -950,9 +950,15 @@ public:
//! Load spending key metadata (used by LoadWallet)
bool LoadZKeyMetadata(const libzcash::PaymentAddress &addr, const CKeyMetadata &meta);
//! Adds an encrypted spending key to the store, without saving it to disk (used by LoadWallet)
bool LoadCryptedZKey(const libzcash::PaymentAddress &addr, const libzcash::ViewingKey &vk, const std::vector<unsigned char> &vchCryptedSecret);
bool LoadCryptedZKey(const libzcash::PaymentAddress &addr, const libzcash::ReceivingKey &rk, const std::vector<unsigned char> &vchCryptedSecret);
//! Adds an encrypted spending key to the store, and saves it to disk (virtual method, declared in crypter.h)
bool AddCryptedSpendingKey(const libzcash::PaymentAddress &address, const libzcash::ViewingKey &vk, const std::vector<unsigned char> &vchCryptedSecret);
bool AddCryptedSpendingKey(const libzcash::PaymentAddress &address, const libzcash::ReceivingKey &rk, const std::vector<unsigned char> &vchCryptedSecret);
//! Adds a viewing key to the store, and saves it to disk.
bool AddViewingKey(const libzcash::ViewingKey &vk);
bool RemoveViewingKey(const libzcash::ViewingKey &vk);
//! Adds a viewing key to the store, without saving it to disk (used by LoadWallet)
bool LoadViewingKey(const libzcash::ViewingKey &dest);
/**
* Increment the next transaction order id
@@ -1115,7 +1121,11 @@ public:
void SetBroadcastTransactions(bool broadcast) { fBroadcastTransactions = broadcast; }
/* Find notes filtered by payment address, min depth, ability to spend */
void GetFilteredNotes(std::vector<CNotePlaintextEntry> & outEntries, std::string address, int minDepth=1, bool ignoreSpent=true);
void GetFilteredNotes(std::vector<CNotePlaintextEntry> & outEntries,
std::string address,
int minDepth=1,
bool ignoreSpent=true,
bool ignoreUnspendable=true);
};

View File

@@ -106,7 +106,7 @@ bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey,
}
bool CWalletDB::WriteCryptedZKey(const libzcash::PaymentAddress & addr,
const libzcash::ViewingKey &vk,
const libzcash::ReceivingKey &rk,
const std::vector<unsigned char>& vchCryptedSecret,
const CKeyMetadata &keyMeta)
{
@@ -116,7 +116,7 @@ bool CWalletDB::WriteCryptedZKey(const libzcash::PaymentAddress & addr,
if (!Write(std::make_pair(std::string("zkeymeta"), addr), keyMeta))
return false;
if (!Write(std::make_pair(std::string("czkey"), addr), std::make_pair(vk, vchCryptedSecret), false))
if (!Write(std::make_pair(std::string("czkey"), addr), std::make_pair(rk, vchCryptedSecret), false))
return false;
if (fEraseUnencryptedKey)
{
@@ -142,6 +142,18 @@ bool CWalletDB::WriteZKey(const libzcash::PaymentAddress& addr, const libzcash::
return Write(std::make_pair(std::string("zkey"), addr), key, false);
}
bool CWalletDB::WriteViewingKey(const libzcash::ViewingKey &vk)
{
nWalletDBUpdated++;
return Write(std::make_pair(std::string("vkey"), vk), '1');
}
bool CWalletDB::EraseViewingKey(const libzcash::ViewingKey &vk)
{
nWalletDBUpdated++;
return Erase(std::make_pair(std::string("vkey"), vk));
}
bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript)
{
nWalletDBUpdated++;
@@ -471,6 +483,19 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
// so set the wallet birthday to the beginning of time.
pwallet->nTimeFirstKey = 1;
}
else if (strType == "vkey")
{
libzcash::ViewingKey vk;
ssKey >> vk;
char fYes;
ssValue >> fYes;
if (fYes == '1')
pwallet->LoadViewingKey(vk);
// Viewing keys have no birthday information for now,
// so set the wallet birthday to the beginning of time.
pwallet->nTimeFirstKey = 1;
}
else if (strType == "zkey")
{
libzcash::PaymentAddress addr;
@@ -585,14 +610,14 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
libzcash::PaymentAddress addr;
ssKey >> addr;
// Deserialization of a pair is just one item after another
uint256 vkValue;
ssValue >> vkValue;
libzcash::ViewingKey vk(vkValue);
uint256 rkValue;
ssValue >> rkValue;
libzcash::ReceivingKey rk(rkValue);
vector<unsigned char> vchCryptedSecret;
ssValue >> vchCryptedSecret;
wss.nCKeys++;
if (!pwallet->LoadCryptedZKey(addr, vk, vchCryptedSecret))
if (!pwallet->LoadCryptedZKey(addr, rk, vchCryptedSecret))
{
strErr = "Error reading wallet database: LoadCryptedZKey failed";
return false;
@@ -694,6 +719,7 @@ static bool IsKeyType(string strType)
{
return (strType== "key" || strType == "wkey" ||
strType == "zkey" || strType == "czkey" ||
strType == "vkey" ||
strType == "mkey" || strType == "ckey");
}

View File

@@ -136,10 +136,13 @@ public:
/// Write spending key to wallet database, where key is payment address and value is spending key.
bool WriteZKey(const libzcash::PaymentAddress& addr, const libzcash::SpendingKey& key, const CKeyMetadata &keyMeta);
bool WriteCryptedZKey(const libzcash::PaymentAddress & addr,
const libzcash::ViewingKey & vk,
const libzcash::ReceivingKey & rk,
const std::vector<unsigned char>& vchCryptedSecret,
const CKeyMetadata &keyMeta);
bool WriteViewingKey(const libzcash::ViewingKey &vk);
bool EraseViewingKey(const libzcash::ViewingKey &vk);
private:
CWalletDB(const CWalletDB&);
void operator=(const CWalletDB&);