diff --git a/src/qt/pivx/coldstakingwidget.cpp b/src/qt/pivx/coldstakingwidget.cpp index d069b5e4963a..e11af5c960ce 100644 --- a/src/qt/pivx/coldstakingwidget.cpp +++ b/src/qt/pivx/coldstakingwidget.cpp @@ -212,10 +212,10 @@ void ColdStakingWidget::loadWalletModel() addressTableModel = walletModel->getAddressTableModel(); addressesFilter = new AddressFilterProxyModel(AddressTableModel::ColdStaking, this); - addressesFilter->sort(sortType, sortOrder); addressesFilter->setSourceModel(addressTableModel); - ui->listViewStakingAddress->setModelColumn(AddressTableModel::Address); + addressesFilter->sort(sortType, sortOrder); ui->listViewStakingAddress->setModel(addressesFilter); + ui->listViewStakingAddress->setModelColumn(AddressTableModel::Address); connect(txModel, &TransactionTableModel::txArrived, this, &ColdStakingWidget::onTxArrived); diff --git a/src/sapling/saplingscriptpubkeyman.cpp b/src/sapling/saplingscriptpubkeyman.cpp index 3366baee001b..769cbd5f8bf6 100644 --- a/src/sapling/saplingscriptpubkeyman.cpp +++ b/src/sapling/saplingscriptpubkeyman.cpp @@ -397,31 +397,25 @@ void SaplingScriptPubKeyMan::GetNotes(const std::vector& saplin for (const auto& outpoint : saplingOutpoints) { const auto* wtx = wallet->GetWalletTx(outpoint.hash); if (!wtx) throw std::runtime_error("No transaction available for hash " + outpoint.hash.GetHex()); + const int depth = WITH_LOCK(wallet->cs_wallet, return wtx->GetDepthInMainChain(); ); const auto& it = wtx->mapSaplingNoteData.find(outpoint); if (it != wtx->mapSaplingNoteData.end()) { - const SaplingOutPoint& op = it->first; const SaplingNoteData& nd = it->second; // skip sent notes if (!nd.IsMyNote()) continue; - const libzcash::SaplingIncomingViewingKey& ivk = *(nd.ivk); - - const OutputDescription& outDesc = wtx->tx->sapData->vShieldedOutput[op.n]; - auto maybe_pt = libzcash::SaplingNotePlaintext::decrypt( - outDesc.encCiphertext, - ivk, - outDesc.ephemeralKey, - outDesc.cmu); - assert(static_cast(maybe_pt)); - auto notePt = maybe_pt.get(); - auto maybe_pa = ivk.address(notePt.d); - assert(static_cast(maybe_pa)); - auto pa = maybe_pa.get(); + // recover plaintext and address + auto optNotePtAndAddress = wtx->DecryptSaplingNote(op); + assert(static_cast(optNotePtAndAddress)); + const libzcash::SaplingIncomingViewingKey& ivk = *(nd.ivk); + const libzcash::SaplingNotePlaintext& notePt = optNotePtAndAddress->first; + const libzcash::SaplingPaymentAddress& pa = optNotePtAndAddress->second; auto note = notePt.note(ivk).get(); - saplingEntriesRet.emplace_back(op, pa, note, notePt.memo(), wtx->GetDepthInMainChain()); + + saplingEntriesRet.emplace_back(op, pa, note, notePt.memo(), depth); } } } @@ -471,9 +465,9 @@ void SaplingScriptPubKeyMan::GetFilteredNotes( } // Filter the transactions before checking for notes + const int depth = wtx.GetDepthInMainChain(); if (!IsFinalTx(wtx.tx, wallet->GetLastBlockHeight() + 1, GetAdjustedTime()) || - wtx.GetDepthInMainChain() < minDepth || - wtx.GetDepthInMainChain() > maxDepth) { + depth < minDepth || depth > maxDepth) { continue; } @@ -481,21 +475,17 @@ void SaplingScriptPubKeyMan::GetFilteredNotes( const SaplingOutPoint& op = it.first; const SaplingNoteData& nd = it.second; - // Skip sent notes + // skip sent notes if (!nd.IsMyNote()) continue; - const libzcash::SaplingIncomingViewingKey& ivk = *(nd.ivk); - auto maybe_pt = libzcash::SaplingNotePlaintext::decrypt( - wtx.tx->sapData->vShieldedOutput[op.n].encCiphertext, - ivk, - wtx.tx->sapData->vShieldedOutput[op.n].ephemeralKey, - wtx.tx->sapData->vShieldedOutput[op.n].cmu); - assert(static_cast(maybe_pt)); - auto notePt = maybe_pt.get(); + // recover plaintext and address + auto optNotePtAndAddress = wtx.DecryptSaplingNote(op); + assert(static_cast(optNotePtAndAddress)); - auto maybe_pa = ivk.address(notePt.d); - assert(static_cast(maybe_pa)); - auto pa = maybe_pa.get(); + const libzcash::SaplingIncomingViewingKey& ivk = *(nd.ivk); + const libzcash::SaplingNotePlaintext& notePt = optNotePtAndAddress->first; + const libzcash::SaplingPaymentAddress& pa = optNotePtAndAddress->second; + auto note = notePt.note(ivk).get(); // skip notes which belong to a different payment address in the wallet if (!(filterAddresses.empty() || filterAddresses.count(pa))) { @@ -516,8 +506,7 @@ void SaplingScriptPubKeyMan::GetFilteredNotes( // continue; //} - auto note = notePt.note(ivk).get(); - saplingEntries.emplace_back(op, pa, note, notePt.memo(), wtx.GetDepthInMainChain()); + saplingEntries.emplace_back(op, pa, note, notePt.memo(), depth); } } } diff --git a/src/test/librust/sapling_wallet_tests.cpp b/src/test/librust/sapling_wallet_tests.cpp index af0cc84688ae..d0f4e3da6c66 100644 --- a/src/test/librust/sapling_wallet_tests.cpp +++ b/src/test/librust/sapling_wallet_tests.cpp @@ -1148,6 +1148,102 @@ BOOST_AUTO_TEST_CASE(MarkAffectedSaplingTransactionsDirty) { RegtestDeactivateSapling(); } +BOOST_AUTO_TEST_CASE(GetNotes) +{ + auto consensusParams = RegtestActivateSapling(); + + CWallet& wallet = *pwalletMain; + libzcash::SaplingPaymentAddress pk; + uint256 blockHash; + std::vector saplingOutpoints; + { + LOCK2(cs_main, wallet.cs_wallet); + setupWallet(wallet); + + // Generate Sapling address + auto sk = GetTestMasterSaplingSpendingKey(); + auto extfvk = sk.ToXFVK(); + pk = sk.DefaultAddress(); + + BOOST_CHECK(wallet.AddSaplingZKey(sk)); + BOOST_CHECK(wallet.HaveSaplingSpendingKey(extfvk)); + + // Set up transparent address + CBasicKeyStore keystore; + CKey tsk = AddTestCKeyToKeyStore(keystore); + auto scriptPubKey = GetScriptForDestination(tsk.GetPubKey().GetID()); + + // Generate shielding tx from transparent to Sapling (five 1 PIV notes) + auto builder = TransactionBuilder(consensusParams, 1, &keystore); + builder.AddTransparentInput(COutPoint(), scriptPubKey, 510000000); + for (int i=0; i<5; i++) builder.AddSaplingOutput(extfvk.fvk.ovk, pk, 100000000, {}); + builder.SetFee(10000000); + auto tx1 = builder.Build().GetTxOrThrow(); + + CWalletTx wtx {&wallet, MakeTransactionRef(tx1)}; + + // Fake-mine the transaction + BOOST_CHECK_EQUAL(0, chainActive.Height()); + SaplingMerkleTree saplingTree; + CBlock block; + block.vtx.emplace_back(wtx.tx); + block.hashMerkleRoot = BlockMerkleRoot(block); + blockHash = block.GetHash(); + CBlockIndex fakeIndex {block}; + BlockMap::iterator mi = mapBlockIndex.emplace(blockHash, &fakeIndex).first; + fakeIndex.phashBlock = &((*mi).first); + chainActive.SetTip(&fakeIndex); + BOOST_CHECK(chainActive.Contains(&fakeIndex)); + BOOST_CHECK_EQUAL(0, chainActive.Height()); + + // Simulate SyncTransaction which calls AddToWalletIfInvolvingMe + auto saplingNoteData = wallet.GetSaplingScriptPubKeyMan()->FindMySaplingNotes(*wtx.tx).first; + BOOST_CHECK(saplingNoteData.size() > 0); + wtx.SetSaplingNoteData(saplingNoteData); + wtx.m_confirm = CWalletTx::Confirmation(CWalletTx::Status::CONFIRMED, fakeIndex.nHeight, block.GetHash(), 0); + wallet.LoadToWallet(wtx); + + // Simulate receiving new block and ChainTip signal + wallet.IncrementNoteWitnesses(&fakeIndex, &block, saplingTree); + wallet.GetSaplingScriptPubKeyMan()->UpdateSaplingNullifierNoteMapForBlock(&block); + wallet.SetLastBlockProcessed(mi->second); + + const uint256& txid = wtx.GetHash(); + for (int i=0; i<5; i++) saplingOutpoints.emplace_back(txid, i); + } + + // Check GetFilteredNotes + std::vector entries; + Optional address = pk; + wallet.GetSaplingScriptPubKeyMan()->GetFilteredNotes(entries, address, 0, true, false); + for (int i=0; i<5; i++) { + BOOST_CHECK(entries[i].op == saplingOutpoints[i]); + BOOST_CHECK(entries[i].address == pk); + BOOST_CHECK_EQUAL(entries[i].confirmations, 1); + } + + // Check GetNotes + std::vector entries2; + wallet.GetSaplingScriptPubKeyMan()->GetNotes(saplingOutpoints, entries2); + for (int i=0; i<5; i++) { + BOOST_CHECK(entries2[i].op == entries[i].op); + BOOST_CHECK(entries2[i].address == entries[i].address); + BOOST_CHECK(entries2[i].note.d == entries[i].note.d); + BOOST_CHECK_EQUAL(entries2[i].note.pk_d, entries[i].note.pk_d); + BOOST_CHECK_EQUAL(entries2[i].note.r, entries[i].note.r); + BOOST_CHECK(entries2[i].memo == entries[i].memo); + BOOST_CHECK_EQUAL(entries2[i].confirmations, entries[i].confirmations); + } + + // Tear down + LOCK(cs_main); + chainActive.SetTip(nullptr); + mapBlockIndex.erase(blockHash); + + // Revert to default + RegtestDeactivateSapling(); +} + // TODO: Back port WriteWitnessCache & SetBestChainIgnoresTxsWithoutShieldedData test cases. BOOST_AUTO_TEST_SUITE_END() diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 862a7cd6600e..e7712777cc9b 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -4783,7 +4783,7 @@ void CWalletTx::SetSaplingNoteData(mapSaplingNoteData_t ¬eData) Optional> CWalletTx::DecryptSaplingNote(SaplingOutPoint op) const + libzcash::SaplingPaymentAddress>> CWalletTx::DecryptSaplingNote(const SaplingOutPoint& op) const { // Check whether we can decrypt this SaplingOutPoint with the ivk auto it = this->mapSaplingNoteData.find(op); @@ -4811,8 +4811,7 @@ Optional> CWalletTx::RecoverSaplingNote( - SaplingOutPoint op, std::set& ovks) const + libzcash::SaplingPaymentAddress>> CWalletTx::RecoverSaplingNote(const SaplingOutPoint& op, const std::set& ovks) const { auto output = this->tx->sapData->vShieldedOutput[op.n]; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 100221e9b3d8..eb5328365763 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -450,16 +450,15 @@ class CWalletTx void BindWallet(CWallet* pwalletIn); - void SetSaplingNoteData(mapSaplingNoteData_t ¬eData); + void SetSaplingNoteData(mapSaplingNoteData_t& noteData); Optional> DecryptSaplingNote(SaplingOutPoint op) const; + libzcash::SaplingPaymentAddress>> DecryptSaplingNote(const SaplingOutPoint& op) const; Optional> RecoverSaplingNote( - SaplingOutPoint op, std::set& ovks) const; + libzcash::SaplingPaymentAddress>> RecoverSaplingNote(const SaplingOutPoint& op, const std::set& ovks) const; //! checks whether a tx has P2CS inputs or not bool HasP2CSInputs() const;