Latest Zcash updates and more CC for N@S

This commit is contained in:
miketout
2018-10-05 00:26:06 -07:00
26 changed files with 657 additions and 154 deletions

View File

@@ -82,8 +82,10 @@ AsyncRPCOperation_mergetoaddress::AsyncRPCOperation_mergetoaddress(
auto address = DecodePaymentAddress(std::get<0>(recipient));
if (IsValidPaymentAddress(address)) {
isToZaddr_ = true;
// TODO: Add Sapling support. For now, ensure we can later convert freely.
assert(boost::get<libzcash::SproutPaymentAddress>(&address) != nullptr);
// TODO: Add Sapling support. For now, return an error to the user.
if (boost::get<libzcash::SproutPaymentAddress>(&address) == nullptr) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Currently, only Sprout zaddrs are supported");
}
toPaymentAddress_ = address;
} else {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid recipient address");
@@ -328,8 +330,10 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
// Copy zinputs to more flexible containers
std::deque<MergeToAddressInputNote> zInputsDeque;
for (auto o : noteInputs_) {
// TODO: Add Sapling support. For now, ensure we can later convert freely.
assert(boost::get<libzcash::SproutSpendingKey>(&std::get<3>(o)) != nullptr);
// TODO: Add Sapling support. For now, return an error to the user.
if (boost::get<libzcash::SproutSpendingKey>(&std::get<3>(o)) == nullptr) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Currently, only Sprout zaddrs are supported");
}
zInputsDeque.push_back(o);
}

View File

@@ -451,7 +451,7 @@ bool CCryptoKeyStore::AddSproutSpendingKey(const libzcash::SproutSpendingKey &sk
bool CCryptoKeyStore::AddSaplingSpendingKey(
const libzcash::SaplingExtendedSpendingKey &sk,
const boost::optional<libzcash::SaplingPaymentAddress> &defaultAddr)
const libzcash::SaplingPaymentAddress &defaultAddr)
{
{
LOCK(cs_SpendingKeyStore);
@@ -498,7 +498,7 @@ bool CCryptoKeyStore::AddCryptedSproutSpendingKey(
bool CCryptoKeyStore::AddCryptedSaplingSpendingKey(
const libzcash::SaplingFullViewingKey &fvk,
const std::vector<unsigned char> &vchCryptedSecret,
const boost::optional<libzcash::SaplingPaymentAddress> &defaultAddr)
const libzcash::SaplingPaymentAddress &defaultAddr)
{
{
LOCK(cs_SpendingKeyStore);
@@ -507,7 +507,7 @@ bool CCryptoKeyStore::AddCryptedSaplingSpendingKey(
}
// if SaplingFullViewingKey is not in SaplingFullViewingKeyMap, add it
if (!AddSaplingFullViewingKey(fvk, defaultAddr)){
if (!AddSaplingFullViewingKey(fvk, defaultAddr)) {
return false;
}
@@ -616,7 +616,7 @@ bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial& vMasterKeyIn)
if (!EncryptSecret(vMasterKeyIn, vchSecret, fvk.GetFingerprint(), vchCryptedSecret)) {
return false;
}
if (!AddCryptedSaplingSpendingKey(fvk, vchCryptedSecret)) {
if (!AddCryptedSaplingSpendingKey(fvk, vchCryptedSecret, sk.DefaultAddress())) {
return false;
}
}

View File

@@ -243,10 +243,10 @@ public:
virtual bool AddCryptedSaplingSpendingKey(
const libzcash::SaplingFullViewingKey &fvk,
const std::vector<unsigned char> &vchCryptedSecret,
const boost::optional<libzcash::SaplingPaymentAddress> &defaultAddr = boost::none);
const libzcash::SaplingPaymentAddress &defaultAddr);
bool AddSaplingSpendingKey(
const libzcash::SaplingExtendedSpendingKey &sk,
const boost::optional<libzcash::SaplingPaymentAddress> &defaultAddr = boost::none);
const libzcash::SaplingPaymentAddress &defaultAddr);
bool HaveSaplingSpendingKey(const libzcash::SaplingFullViewingKey &fvk) const
{
{

View File

@@ -512,13 +512,13 @@ TEST(WalletTests, FindMySaplingNotes) {
// No Sapling notes can be found in tx which does not belong to the wallet
CWalletTx wtx {&wallet, tx};
ASSERT_FALSE(wallet.HaveSaplingSpendingKey(fvk));
auto noteMap = wallet.FindMySaplingNotes(wtx);
auto noteMap = wallet.FindMySaplingNotes(wtx).first;
EXPECT_EQ(0, noteMap.size());
// Add spending key to wallet, so Sapling notes can be found
ASSERT_TRUE(wallet.AddSaplingZKey(sk));
ASSERT_TRUE(wallet.AddSaplingZKey(sk, pk));
ASSERT_TRUE(wallet.HaveSaplingSpendingKey(fvk));
noteMap = wallet.FindMySaplingNotes(wtx);
noteMap = wallet.FindMySaplingNotes(wtx).first;
EXPECT_EQ(2, noteMap.size());
// Revert to default
@@ -630,7 +630,7 @@ TEST(WalletTests, GetConflictedSaplingNotes) {
auto ivk = fvk.in_viewing_key();
auto pk = sk.DefaultAddress();
ASSERT_TRUE(wallet.AddSaplingZKey(sk));
ASSERT_TRUE(wallet.AddSaplingZKey(sk, pk));
ASSERT_TRUE(wallet.HaveSaplingSpendingKey(fvk));
// Generate note A
@@ -664,7 +664,7 @@ TEST(WalletTests, GetConflictedSaplingNotes) {
EXPECT_EQ(0, chainActive.Height());
// Simulate SyncTransaction which calls AddToWalletIfInvolvingMe
auto saplingNoteData = wallet.FindMySaplingNotes(wtx);
auto saplingNoteData = wallet.FindMySaplingNotes(wtx).first;
ASSERT_TRUE(saplingNoteData.size() > 0);
wtx.SetSaplingNoteData(saplingNoteData);
wtx.SetMerkleBranch(block);
@@ -815,7 +815,7 @@ TEST(WalletTests, SaplingNullifierIsSpent) {
auto tx = maybe_tx.get();
CWalletTx wtx {&wallet, tx};
ASSERT_TRUE(wallet.AddSaplingZKey(sk));
ASSERT_TRUE(wallet.AddSaplingZKey(sk, pk));
ASSERT_TRUE(wallet.HaveSaplingSpendingKey(fvk));
// Manually compute the nullifier based on the known position
@@ -912,7 +912,7 @@ TEST(WalletTests, NavigateFromSaplingNullifierToNote) {
auto tx = maybe_tx.get();
CWalletTx wtx {&wallet, tx};
ASSERT_TRUE(wallet.AddSaplingZKey(sk));
ASSERT_TRUE(wallet.AddSaplingZKey(sk, pk));
ASSERT_TRUE(wallet.HaveSaplingSpendingKey(fvk));
// Manually compute the nullifier based on the expected position
@@ -938,7 +938,7 @@ TEST(WalletTests, NavigateFromSaplingNullifierToNote) {
// Simulate SyncTransaction which calls AddToWalletIfInvolvingMe
wtx.SetMerkleBranch(block);
auto saplingNoteData = wallet.FindMySaplingNotes(wtx);
auto saplingNoteData = wallet.FindMySaplingNotes(wtx).first;
ASSERT_TRUE(saplingNoteData.size() > 0);
wtx.SetSaplingNoteData(saplingNoteData);
wallet.AddToWallet(wtx, true, NULL);
@@ -1048,7 +1048,7 @@ TEST(WalletTests, SpentSaplingNoteIsFromMe) {
auto tx = maybe_tx.get();
CWalletTx wtx {&wallet, tx};
ASSERT_TRUE(wallet.AddSaplingZKey(sk));
ASSERT_TRUE(wallet.AddSaplingZKey(sk, pk));
ASSERT_TRUE(wallet.HaveSaplingSpendingKey(fvk));
// Fake-mine the transaction
@@ -1064,7 +1064,7 @@ TEST(WalletTests, SpentSaplingNoteIsFromMe) {
EXPECT_TRUE(chainActive.Contains(&fakeIndex));
EXPECT_EQ(0, chainActive.Height());
auto saplingNoteData = wallet.FindMySaplingNotes(wtx);
auto saplingNoteData = wallet.FindMySaplingNotes(wtx).first;
ASSERT_TRUE(saplingNoteData.size() > 0);
wtx.SetSaplingNoteData(saplingNoteData);
wtx.SetMerkleBranch(block);
@@ -1141,7 +1141,7 @@ TEST(WalletTests, SpentSaplingNoteIsFromMe) {
EXPECT_TRUE(chainActive.Contains(&fakeIndex2));
EXPECT_EQ(1, chainActive.Height());
auto saplingNoteData2 = wallet.FindMySaplingNotes(wtx2);
auto saplingNoteData2 = wallet.FindMySaplingNotes(wtx2).first;
ASSERT_TRUE(saplingNoteData2.size() > 0);
wtx2.SetSaplingNoteData(saplingNoteData2);
wtx2.SetMerkleBranch(block2);
@@ -1751,7 +1751,7 @@ TEST(WalletTests, UpdatedSaplingNoteData) {
// Wallet contains fvk1 but not fvk2
CWalletTx wtx {&wallet, tx};
ASSERT_TRUE(wallet.AddSaplingZKey(sk));
ASSERT_TRUE(wallet.AddSaplingZKey(sk, pk));
ASSERT_TRUE(wallet.HaveSaplingSpendingKey(fvk));
ASSERT_FALSE(wallet.HaveSaplingSpendingKey(fvk2));
@@ -1769,7 +1769,7 @@ TEST(WalletTests, UpdatedSaplingNoteData) {
EXPECT_EQ(0, chainActive.Height());
// Simulate SyncTransaction which calls AddToWalletIfInvolvingMe
auto saplingNoteData = wallet.FindMySaplingNotes(wtx);
auto saplingNoteData = wallet.FindMySaplingNotes(wtx).first;
ASSERT_TRUE(saplingNoteData.size() == 1); // wallet only has key for change output
wtx.SetSaplingNoteData(saplingNoteData);
wtx.SetMerkleBranch(block);
@@ -1784,10 +1784,10 @@ TEST(WalletTests, UpdatedSaplingNoteData) {
wtx = wallet.mapWallet[hash];
// Now lets add key fvk2 so wallet can find the payment note sent to pk2
ASSERT_TRUE(wallet.AddSaplingZKey(sk2));
ASSERT_TRUE(wallet.AddSaplingZKey(sk2, pk2));
ASSERT_TRUE(wallet.HaveSaplingSpendingKey(fvk2));
CWalletTx wtx2 = wtx;
auto saplingNoteData2 = wallet.FindMySaplingNotes(wtx2);
auto saplingNoteData2 = wallet.FindMySaplingNotes(wtx2).first;
ASSERT_TRUE(saplingNoteData2.size() == 2);
wtx2.SetSaplingNoteData(saplingNoteData2);
@@ -1881,7 +1881,7 @@ TEST(WalletTests, MarkAffectedSaplingTransactionsDirty) {
auto ivk = fvk.in_viewing_key();
auto pk = sk.DefaultAddress();
ASSERT_TRUE(wallet.AddSaplingZKey(sk));
ASSERT_TRUE(wallet.AddSaplingZKey(sk, pk));
ASSERT_TRUE(wallet.HaveSaplingSpendingKey(fvk));
// Set up transparent address
@@ -1923,7 +1923,7 @@ TEST(WalletTests, MarkAffectedSaplingTransactionsDirty) {
EXPECT_EQ(0, chainActive.Height());
// Simulate SyncTransaction which calls AddToWalletIfInvolvingMe
auto saplingNoteData = wallet.FindMySaplingNotes(wtx);
auto saplingNoteData = wallet.FindMySaplingNotes(wtx).first;
ASSERT_TRUE(saplingNoteData.size() > 0);
wtx.SetSaplingNoteData(saplingNoteData);
wtx.SetMerkleBranch(block);

View File

@@ -724,8 +724,10 @@ UniValue z_importviewingkey(const UniValue& params, bool fHelp)
if (!IsValidViewingKey(viewingkey)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid viewing key");
}
// TODO: Add Sapling support. For now, ensure we can freely convert.
assert(boost::get<libzcash::SproutViewingKey>(&viewingkey) != nullptr);
// TODO: Add Sapling support. For now, return an error to the user.
if (boost::get<libzcash::SproutViewingKey>(&viewingkey) == nullptr) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Currently, only Sprout viewing keys are supported");
}
auto vkey = boost::get<libzcash::SproutViewingKey>(viewingkey);
auto addr = vkey.address();
@@ -824,8 +826,10 @@ UniValue z_exportviewingkey(const UniValue& params, bool fHelp)
if (!IsValidPaymentAddress(address)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr");
}
// TODO: Add Sapling support. For now, ensure we can freely convert.
assert(boost::get<libzcash::SproutPaymentAddress>(&address) != nullptr);
// TODO: Add Sapling support. For now, return an error to the user.
if (boost::get<libzcash::SproutPaymentAddress>(&address) == nullptr) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Currently, only Sprout zaddrs are supported");
}
auto addr = boost::get<libzcash::SproutPaymentAddress>(address);
libzcash::SproutViewingKey vk;

View File

@@ -2799,12 +2799,13 @@ UniValue z_listunspent(const UniValue& params, bool fHelp)
"Optionally filter to only include notes sent to specified addresses.\n"
"When minconf is 0, unspent notes with zero confirmations are returned, even though they are not immediately spendable.\n"
"Results are an array of Objects, each of which has:\n"
"{txid, jsindex, jsoutindex, confirmations, address, amount, memo}\n"
"{txid, jsindex, jsoutindex, confirmations, address, amount, memo} (Sprout)\n"
"{txid, outindex, confirmations, address, amount, memo} (Sapling)\n"
"\nArguments:\n"
"1. minconf (numeric, optional, default=1) The minimum confirmations to filter\n"
"2. maxconf (numeric, optional, default=9999999) The maximum confirmations to filter\n"
"3. includeWatchonly (bool, optional, default=false) Also include watchonly addresses (see 'z_importviewingkey')\n"
"4. \"addresses\" (string) A json array of zaddrs to filter on. Duplicate addresses not allowed.\n"
"4. \"addresses\" (string) A json array of zaddrs (both Sprout and Sapling) to filter on. Duplicate addresses not allowed.\n"
" [\n"
" \"address\" (string) zaddr\n"
" ,...\n"
@@ -2814,7 +2815,8 @@ UniValue z_listunspent(const UniValue& params, bool fHelp)
" {\n"
" \"txid\" : \"txid\", (string) the transaction id \n"
" \"jsindex\" : n (numeric) the joinsplit index\n"
" \"jsoutindex\" : n (numeric) the output index of the joinsplit\n"
" \"jsoutindex\" (sprout) : n (numeric) the output index of the joinsplit\n"
" \"outindex\" (sapling) : n (numeric) the output index\n"
" \"confirmations\" : n (numeric) the number of confirmations\n"
" \"spendable\" : true|false (boolean) true if note can be spent by wallet, false if note has zero confirmations, false if address is watchonly\n"
" \"address\" : \"address\", (string) the shielded address\n"
@@ -2874,17 +2876,14 @@ UniValue z_listunspent(const UniValue& params, bool fHelp)
}
string address = o.get_str();
auto zaddr = DecodePaymentAddress(address);
if (IsValidPaymentAddress(zaddr)) {
// TODO: Add Sapling support. For now, ensure we can freely convert.
assert(boost::get<libzcash::SproutPaymentAddress>(&zaddr) != nullptr);
libzcash::SproutPaymentAddress addr = boost::get<libzcash::SproutPaymentAddress>(zaddr);
if (!fIncludeWatchonly && !pwalletMain->HaveSproutSpendingKey(addr)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, spending key for address does not belong to wallet: ") + address);
}
zaddrs.insert(addr);
} else {
if (!IsValidPaymentAddress(zaddr)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, address is not a valid zaddr: ") + address);
}
auto hasSpendingKey = boost::apply_visitor(HaveSpendingKeyForPaymentAddress(pwalletMain), zaddr);
if (!fIncludeWatchonly && !hasSpendingKey) {
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, spending key for address does not belong to wallet: ") + address);
}
zaddrs.insert(zaddr);
if (setAddress.count(address)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ") + address);
@@ -2894,10 +2893,15 @@ UniValue z_listunspent(const UniValue& params, bool fHelp)
}
else {
// User did not provide zaddrs, so use default i.e. all addresses
// TODO: Add Sapling support
std::set<libzcash::SproutPaymentAddress> sproutzaddrs = {};
pwalletMain->GetSproutPaymentAddresses(sproutzaddrs);
// Sapling support
std::set<libzcash::SaplingPaymentAddress> saplingzaddrs = {};
pwalletMain->GetSaplingPaymentAddresses(saplingzaddrs);
zaddrs.insert(sproutzaddrs.begin(), sproutzaddrs.end());
zaddrs.insert(saplingzaddrs.begin(), saplingzaddrs.end());
}
UniValue results(UniValue::VARR);
@@ -2907,6 +2911,7 @@ UniValue z_listunspent(const UniValue& params, bool fHelp)
std::vector<UnspentSaplingNoteEntry> saplingEntries;
pwalletMain->GetUnspentFilteredNotes(sproutEntries, saplingEntries, zaddrs, nMinDepth, nMaxDepth, !fIncludeWatchonly);
std::set<std::pair<PaymentAddress, uint256>> nullifierSet = pwalletMain->GetNullifiersForAddresses(zaddrs);
for (CUnspentSproutNotePlaintextEntry & entry : sproutEntries) {
UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("txid", entry.jsop.hash.ToString()));
@@ -2920,11 +2925,30 @@ UniValue z_listunspent(const UniValue& params, bool fHelp)
std::string data(entry.plaintext.memo().begin(), entry.plaintext.memo().end());
obj.push_back(Pair("memo", HexStr(data)));
if (hasSproutSpendingKey) {
obj.push_back(Pair("change", pwalletMain->IsNoteChange(nullifierSet, entry.address, entry.jsop)));
obj.push_back(Pair("change", pwalletMain->IsNoteSproutChange(nullifierSet, entry.address, entry.jsop)));
}
results.push_back(obj);
}
for (UnspentSaplingNoteEntry & entry : saplingEntries) {
UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("txid", entry.op.hash.ToString()));
obj.push_back(Pair("outindex", (int)entry.op.n));
obj.push_back(Pair("confirmations", entry.nHeight));
libzcash::SaplingIncomingViewingKey ivk;
libzcash::SaplingFullViewingKey fvk;
pwalletMain->GetSaplingIncomingViewingKey(boost::get<libzcash::SaplingPaymentAddress>(entry.address), ivk);
pwalletMain->GetSaplingFullViewingKey(ivk, fvk);
bool hasSaplingSpendingKey = pwalletMain->HaveSaplingSpendingKey(fvk);
obj.push_back(Pair("spendable", hasSaplingSpendingKey));
obj.push_back(Pair("address", EncodePaymentAddress(entry.address)));
obj.push_back(Pair("amount", ValueFromAmount(CAmount(entry.note.value())))); // note.value() is equivalent to plaintext.value()
obj.push_back(Pair("memo", HexStr(entry.memo)));
if (hasSaplingSpendingKey) {
obj.push_back(Pair("change", pwalletMain->IsNoteSaplingChange(nullifierSet, entry.address, entry.op)));
}
results.push_back(obj);
}
// TODO: Sapling
}
return results;
@@ -3486,7 +3510,7 @@ UniValue z_getnewaddress(const UniValue& params, bool fHelp)
if (addrType == ADDR_TYPE_SPROUT) {
return EncodePaymentAddress(pwalletMain->GenerateNewZKey());
} else if (addrType == ADDR_TYPE_SAPLING && allowSapling) {
} else if (addrType == ADDR_TYPE_SAPLING) {
return EncodePaymentAddress(pwalletMain->GenerateNewSaplingZKey());
} else {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid address type");
@@ -3650,12 +3674,9 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp)
if (!IsValidPaymentAddress(zaddr)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr.");
}
// TODO: Add Sapling support. For now, ensure we can freely convert.
assert(boost::get<libzcash::SproutPaymentAddress>(&zaddr) != nullptr);
auto sproutzaddr = boost::get<libzcash::SproutPaymentAddress>(zaddr);
bool hasSproutSpendingKey = pwalletMain->HaveSproutSpendingKey(sproutzaddr);
if (!(hasSproutSpendingKey || pwalletMain->HaveSproutViewingKey(sproutzaddr))) {
// Visitor to support Sprout and Sapling addrs
if (!boost::apply_visitor(PaymentAddressBelongsToWallet(pwalletMain), zaddr)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, zaddr spending key or viewing key not found.");
}
@@ -3663,22 +3684,40 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp)
std::vector<CSproutNotePlaintextEntry> sproutEntries;
std::vector<SaplingNoteEntry> saplingEntries;
pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, fromaddress, nMinDepth, false, false);
auto nullifierSet = hasSproutSpendingKey ? pwalletMain->GetNullifiersForAddresses({zaddr}) : std::set<std::pair<PaymentAddress, uint256>>();
for (CSproutNotePlaintextEntry & entry : sproutEntries) {
UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("txid", entry.jsop.hash.ToString()));
obj.push_back(Pair("amount", ValueFromAmount(CAmount(entry.plaintext.value()))));
std::string data(entry.plaintext.memo().begin(), entry.plaintext.memo().end());
obj.push_back(Pair("memo", HexStr(data)));
// (txid, jsindex, jsoutindex) is needed to globally identify a note
obj.push_back(Pair("jsindex", entry.jsop.js));
obj.push_back(Pair("jsoutindex", entry.jsop.n));
if (hasSproutSpendingKey) {
obj.push_back(Pair("change", pwalletMain->IsNoteChange(nullifierSet, entry.address, entry.jsop)));
}
result.push_back(obj);
std::set<std::pair<PaymentAddress, uint256>> nullifierSet;
auto hasSpendingKey = boost::apply_visitor(HaveSpendingKeyForPaymentAddress(pwalletMain), zaddr);
if (hasSpendingKey) {
nullifierSet = pwalletMain->GetNullifiersForAddresses({zaddr});
}
if (boost::get<libzcash::SproutPaymentAddress>(&zaddr) != nullptr) {
for (CSproutNotePlaintextEntry & entry : sproutEntries) {
UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("txid", entry.jsop.hash.ToString()));
obj.push_back(Pair("amount", ValueFromAmount(CAmount(entry.plaintext.value()))));
std::string data(entry.plaintext.memo().begin(), entry.plaintext.memo().end());
obj.push_back(Pair("memo", HexStr(data)));
obj.push_back(Pair("jsindex", entry.jsop.js));
obj.push_back(Pair("jsoutindex", entry.jsop.n));
if (hasSpendingKey) {
obj.push_back(Pair("change", pwalletMain->IsNoteSproutChange(nullifierSet, entry.address, entry.jsop)));
}
result.push_back(obj);
}
} else if (boost::get<libzcash::SaplingPaymentAddress>(&zaddr) != nullptr) {
for (SaplingNoteEntry & entry : saplingEntries) {
UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("txid", entry.op.hash.ToString()));
obj.push_back(Pair("amount", ValueFromAmount(CAmount(entry.note.value()))));
obj.push_back(Pair("memo", HexStr(entry.memo)));
obj.push_back(Pair("outindex", (int)entry.op.n));
if (hasSpendingKey) {
obj.push_back(Pair("change", pwalletMain->IsNoteSaplingChange(nullifierSet, entry.address, entry.op)));
}
result.push_back(obj);
}
}
// TODO: Sapling
return result;
}

View File

@@ -165,7 +165,7 @@ SaplingPaymentAddress CWallet::GenerateNewSaplingZKey()
// Add spending key to keystore
bool CWallet::AddSaplingZKey(
const libzcash::SaplingExtendedSpendingKey &sk,
const boost::optional<libzcash::SaplingPaymentAddress> &defaultAddr)
const libzcash::SaplingPaymentAddress &defaultAddr)
{
AssertLockHeld(cs_wallet); // mapSaplingZKeyMetadata
@@ -306,7 +306,7 @@ bool CWallet::AddCryptedSproutSpendingKey(
bool CWallet::AddCryptedSaplingSpendingKey(const libzcash::SaplingFullViewingKey &fvk,
const std::vector<unsigned char> &vchCryptedSecret,
const boost::optional<libzcash::SaplingPaymentAddress> &defaultAddr)
const libzcash::SaplingPaymentAddress &defaultAddr)
{
if (!CCryptoKeyStore::AddCryptedSaplingSpendingKey(fvk, vchCryptedSecret, defaultAddr))
return false;
@@ -523,20 +523,50 @@ void CWallet::SetBestChain(const CBlockLocator& loc)
SetBestChainINTERNAL(walletdb, loc);
}
std::set<std::pair<libzcash::PaymentAddress, uint256>> CWallet::GetNullifiersForAddresses(const std::set<libzcash::PaymentAddress> & addresses)
std::set<std::pair<libzcash::PaymentAddress, uint256>> CWallet::GetNullifiersForAddresses(
const std::set<libzcash::PaymentAddress> & addresses)
{
std::set<std::pair<libzcash::PaymentAddress, uint256>> nullifierSet;
// Sapling ivk -> list of addrs map
// (There may be more than one diversified address for a given ivk.)
std::map<libzcash::SaplingIncomingViewingKey, std::vector<libzcash::SaplingPaymentAddress>> ivkMap;
for (const auto & addr : addresses) {
auto saplingAddr = boost::get<libzcash::SaplingPaymentAddress>(&addr);
if (saplingAddr != nullptr) {
libzcash::SaplingIncomingViewingKey ivk;
this->GetSaplingIncomingViewingKey(*saplingAddr, ivk);
ivkMap[ivk].push_back(*saplingAddr);
}
}
for (const auto & txPair : mapWallet) {
// Sprout
for (const auto & noteDataPair : txPair.second.mapSproutNoteData) {
if (noteDataPair.second.nullifier && addresses.count(noteDataPair.second.address)) {
nullifierSet.insert(std::make_pair(noteDataPair.second.address, noteDataPair.second.nullifier.get()));
auto & noteData = noteDataPair.second;
auto & nullifier = noteData.nullifier;
auto & address = noteData.address;
if (nullifier && addresses.count(address)) {
nullifierSet.insert(std::make_pair(address, nullifier.get()));
}
}
// Sapling
for (const auto & noteDataPair : txPair.second.mapSaplingNoteData) {
auto & noteData = noteDataPair.second;
auto & nullifier = noteData.nullifier;
auto & ivk = noteData.ivk;
if (nullifier && ivkMap.count(ivk)) {
for (const auto & addr : ivkMap[ivk]) {
nullifierSet.insert(std::make_pair(addr, nullifier.get()));
}
}
}
}
return nullifierSet;
}
bool CWallet::IsNoteChange(const std::set<std::pair<libzcash::PaymentAddress, uint256>> & nullifierSet, const PaymentAddress & address, const JSOutPoint & jsop)
bool CWallet::IsNoteSproutChange(
const std::set<std::pair<libzcash::PaymentAddress, uint256>> & nullifierSet,
const PaymentAddress & address,
const JSOutPoint & jsop)
{
// A Note is marked as "change" if the address that received it
// also spent Notes in the same transaction. This will catch,
@@ -557,6 +587,26 @@ bool CWallet::IsNoteChange(const std::set<std::pair<libzcash::PaymentAddress, ui
return false;
}
bool CWallet::IsNoteSaplingChange(const std::set<std::pair<libzcash::PaymentAddress, uint256>> & nullifierSet,
const libzcash::PaymentAddress & address,
const SaplingOutPoint & op)
{
// A Note is marked as "change" if the address that received it
// also spent Notes in the same transaction. This will catch,
// for instance:
// - Change created by spending fractions of Notes (because
// z_sendmany sends change to the originating z-address).
// - Notes created by consolidation transactions (e.g. using
// z_mergetoaddress).
// - Notes sent from one address to itself.
for (const SpendDescription &spend : mapWallet[op.hash].vShieldedSpend) {
if (nullifierSet.count(std::make_pair(address, spend.nullifier))) {
return true;
}
}
return false;
}
bool CWallet::SetMinVersion(enum WalletFeature nVersion, CWalletDB* pwalletdbIn, bool fExplicit)
{
LOCK(cs_wallet); // nWalletVersion
@@ -1256,9 +1306,9 @@ int32_t CWallet::VerusStakeTransaction(CBlock *pBlock, CMutableTransaction &txNe
txnouttype whichType;
std::vector<std::vector<unsigned char>> vSolutions;
CBlockIndex *tipindex;
tipindex = chainActive.LastTip();
bool extendedStake = tipindex->GetHeight() >= Params().GetConsensus().vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight;
CBlockIndex *tipindex = chainActive.LastTip();
uint32_t stakeHeight = tipindex->GetHeight() + 1;
bool extendedStake = stakeHeight >= Params().GetConsensus().vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight;
if (!extendedStake)
pk = CPubKey();
@@ -1275,7 +1325,6 @@ int32_t CWallet::VerusStakeTransaction(CBlock *pBlock, CMutableTransaction &txNe
bool signSuccess;
SignatureData sigdata;
uint64_t txfee;
uint32_t stakeHeight = chainActive.Height() + 1;
auto consensusBranchId = CurrentEpochBranchId(stakeHeight, Params().GetConsensus());
const CKeyStore& keystore = *pwalletMain;
@@ -1321,10 +1370,11 @@ int32_t CWallet::VerusStakeTransaction(CBlock *pBlock, CMutableTransaction &txNe
return 0;
txOut1.scriptPubKey << OP_RETURN
<< CStakeParams(pSrcIndex->GetHeight(), tipindex->GetHeight() + 1, pBlock->hashPrevBlock, pk).AsVector();
<< CStakeParams(pSrcIndex->GetHeight(), tipindex->GetHeight() + 1, tipindex->GetBlockHash(), pk).AsVector();
}
nValue = txNew.vout[0].nValue = stakeSource.vout[voutNum].nValue - txfee;
txNew.nLockTime = 0;
CTransaction txNewConst(txNew);
signSuccess = ProduceSignature(TransactionSignatureCreator(&keystore, &txNewConst, 0, nValue, SIGHASH_ALL), stakeSource.vout[voutNum].scriptPubKey, sigdata, consensusBranchId);
@@ -1645,7 +1695,14 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl
bool fExisted = mapWallet.count(tx.GetHash()) != 0;
if (fExisted && !fUpdate) return false;
auto sproutNoteData = FindMySproutNotes(tx);
auto saplingNoteData = FindMySaplingNotes(tx);
auto saplingNoteDataAndAddressesToAdd = FindMySaplingNotes(tx);
auto saplingNoteData = saplingNoteDataAndAddressesToAdd.first;
auto addressesToAdd = saplingNoteDataAndAddressesToAdd.second;
for (const auto &addressToAdd : addressesToAdd) {
if (!AddSaplingIncomingViewingKey(addressToAdd.second, addressToAdd.first)) {
return false;
}
}
if (fExisted || IsMine(tx) || IsFromMe(tx) || sproutNoteData.size() > 0 || saplingNoteData.size() > 0)
{
CWalletTx wtx(this,tx);
@@ -1806,12 +1863,13 @@ mapSproutNoteData_t CWallet::FindMySproutNotes(const CTransaction &tx) const
* the result of FindMySaplingNotes (for the addresses available at the time) will
* already have been cached in CWalletTx.mapSaplingNoteData.
*/
mapSaplingNoteData_t CWallet::FindMySaplingNotes(const CTransaction &tx) const
std::pair<mapSaplingNoteData_t, SaplingIncomingViewingKeyMap> CWallet::FindMySaplingNotes(const CTransaction &tx) const
{
LOCK(cs_SpendingKeyStore);
uint256 hash = tx.GetHash();
mapSaplingNoteData_t noteData;
SaplingIncomingViewingKeyMap viewingKeysToAdd;
// Protocol Spec: 4.19 Block Chain Scanning (Sapling)
for (uint32_t i = 0; i < tx.vShieldedOutput.size(); ++i) {
@@ -1822,6 +1880,10 @@ mapSaplingNoteData_t CWallet::FindMySaplingNotes(const CTransaction &tx) const
if (!result) {
continue;
}
auto address = ivk.address(result.get().d);
if (address && mapSaplingIncomingViewingKeys.count(address.get()) == 0) {
viewingKeysToAdd[address.get()] = ivk;
}
// We don't cache the nullifier here as computing it requires knowledge of the note position
// in the commitment tree, which can only be determined when the transaction has been mined.
SaplingOutPoint op {hash, i};
@@ -1832,7 +1894,7 @@ mapSaplingNoteData_t CWallet::FindMySaplingNotes(const CTransaction &tx) const
}
}
return noteData;
return std::make_pair(noteData, viewingKeysToAdd);
}
bool CWallet::IsSproutNullifierFromMe(const uint256& nullifier) const

View File

@@ -1059,11 +1059,11 @@ public:
//! Adds Sapling spending key to the store, and saves it to disk
bool AddSaplingZKey(
const libzcash::SaplingExtendedSpendingKey &key,
const boost::optional<libzcash::SaplingPaymentAddress> &defaultAddr = boost::none);
const libzcash::SaplingPaymentAddress &defaultAddr);
bool AddCryptedSaplingSpendingKey(
const libzcash::SaplingFullViewingKey &fvk,
const std::vector<unsigned char> &vchCryptedSecret,
const boost::optional<libzcash::SaplingPaymentAddress> &defaultAddr = boost::none);
const libzcash::SaplingPaymentAddress &defaultAddr);
/**
* Increment the next transaction order id
@@ -1133,7 +1133,7 @@ public:
const uint256& hSig,
uint8_t n) const;
mapSproutNoteData_t FindMySproutNotes(const CTransaction& tx) const;
mapSaplingNoteData_t FindMySaplingNotes(const CTransaction& tx) const;
std::pair<mapSaplingNoteData_t, SaplingIncomingViewingKeyMap> FindMySaplingNotes(const CTransaction& tx) const;
bool IsSproutNullifierFromMe(const uint256& nullifier) const;
bool IsSaplingNullifierFromMe(const uint256& nullifier) const;
@@ -1164,7 +1164,8 @@ public:
/** Saves witness caches and best block locator to disk. */
void SetBestChain(const CBlockLocator& loc);
std::set<std::pair<libzcash::PaymentAddress, uint256>> GetNullifiersForAddresses(const std::set<libzcash::PaymentAddress> & addresses);
bool IsNoteChange(const std::set<std::pair<libzcash::PaymentAddress, uint256>> & nullifierSet, const libzcash::PaymentAddress & address, const JSOutPoint & entry);
bool IsNoteSproutChange(const std::set<std::pair<libzcash::PaymentAddress, uint256>> & nullifierSet, const libzcash::PaymentAddress & address, const JSOutPoint & entry);
bool IsNoteSaplingChange(const std::set<std::pair<libzcash::PaymentAddress, uint256>> & nullifierSet, const libzcash::PaymentAddress & address, const SaplingOutPoint & entry);
DBErrors LoadWallet(bool& fFirstRunRet);
DBErrors ZapWalletTx(std::vector<CWalletTx>& vWtx);