Latest Zcash updates and more CC for N@S
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user