Auto merge of #3422 - bitcartel:3061_track_notes_based_on_3062, r=str4d
Track Sapling notes and nullifiers in the wallet (in-memory only, no persistence to disk) Part of #3061. Add in-memory tracking of Sapling notes and nullifiers to the wallet.
This commit is contained in:
commit
20f87bc226
|
@ -35,6 +35,11 @@ TEST(noteencryption, NotePlaintext)
|
||||||
}
|
}
|
||||||
|
|
||||||
SaplingNote note(addr, 39393);
|
SaplingNote note(addr, 39393);
|
||||||
|
auto cmu_opt = note.cm();
|
||||||
|
if (!cmu_opt) {
|
||||||
|
FAIL();
|
||||||
|
}
|
||||||
|
uint256 cmu = cmu_opt.get();
|
||||||
SaplingNotePlaintext pt(note, memo);
|
SaplingNotePlaintext pt(note, memo);
|
||||||
|
|
||||||
auto res = pt.encrypt(addr.pk_d);
|
auto res = pt.encrypt(addr.pk_d);
|
||||||
|
@ -48,11 +53,20 @@ TEST(noteencryption, NotePlaintext)
|
||||||
auto encryptor = enc.second;
|
auto encryptor = enc.second;
|
||||||
auto epk = encryptor.get_epk();
|
auto epk = encryptor.get_epk();
|
||||||
|
|
||||||
// Try to decrypt
|
// Try to decrypt with incorrect commitment
|
||||||
|
ASSERT_FALSE(SaplingNotePlaintext::decrypt(
|
||||||
|
ct,
|
||||||
|
ivk,
|
||||||
|
epk,
|
||||||
|
uint256()
|
||||||
|
));
|
||||||
|
|
||||||
|
// Try to decrypt with correct commitment
|
||||||
auto foo = SaplingNotePlaintext::decrypt(
|
auto foo = SaplingNotePlaintext::decrypt(
|
||||||
ct,
|
ct,
|
||||||
ivk,
|
ivk,
|
||||||
epk
|
epk,
|
||||||
|
cmu
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!foo) {
|
if (!foo) {
|
||||||
|
@ -112,12 +126,24 @@ TEST(noteencryption, NotePlaintext)
|
||||||
ASSERT_TRUE(decrypted_out_ct_unwrapped.pk_d == out_pt.pk_d);
|
ASSERT_TRUE(decrypted_out_ct_unwrapped.pk_d == out_pt.pk_d);
|
||||||
ASSERT_TRUE(decrypted_out_ct_unwrapped.esk == out_pt.esk);
|
ASSERT_TRUE(decrypted_out_ct_unwrapped.esk == out_pt.esk);
|
||||||
|
|
||||||
|
// Test sender won't accept invalid commitments
|
||||||
|
ASSERT_FALSE(
|
||||||
|
SaplingNotePlaintext::decrypt(
|
||||||
|
ct,
|
||||||
|
epk,
|
||||||
|
decrypted_out_ct_unwrapped.esk,
|
||||||
|
decrypted_out_ct_unwrapped.pk_d,
|
||||||
|
uint256()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
// Test sender can decrypt the note ciphertext.
|
// Test sender can decrypt the note ciphertext.
|
||||||
foo = SaplingNotePlaintext::decrypt(
|
foo = SaplingNotePlaintext::decrypt(
|
||||||
ct,
|
ct,
|
||||||
epk,
|
epk,
|
||||||
decrypted_out_ct_unwrapped.esk,
|
decrypted_out_ct_unwrapped.esk,
|
||||||
decrypted_out_ct_unwrapped.pk_d
|
decrypted_out_ct_unwrapped.pk_d,
|
||||||
|
cmu
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!foo) {
|
if (!foo) {
|
||||||
|
|
|
@ -56,7 +56,7 @@ TEST(TransactionBuilder, Invoke)
|
||||||
|
|
||||||
// Prepare to spend the note that was just created
|
// Prepare to spend the note that was just created
|
||||||
auto maybe_pt = libzcash::SaplingNotePlaintext::decrypt(
|
auto maybe_pt = libzcash::SaplingNotePlaintext::decrypt(
|
||||||
tx1.vShieldedOutput[0].encCiphertext, ivk, tx1.vShieldedOutput[0].ephemeralKey);
|
tx1.vShieldedOutput[0].encCiphertext, ivk, tx1.vShieldedOutput[0].ephemeralKey, tx1.vShieldedOutput[0].cm);
|
||||||
ASSERT_EQ(static_cast<bool>(maybe_pt), true);
|
ASSERT_EQ(static_cast<bool>(maybe_pt), true);
|
||||||
auto maybe_note = maybe_pt.get().note(ivk);
|
auto maybe_note = maybe_pt.get().note(ivk);
|
||||||
ASSERT_EQ(static_cast<bool>(maybe_note), true);
|
ASSERT_EQ(static_cast<bool>(maybe_note), true);
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -477,6 +477,7 @@ void CWallet::ChainTip(const CBlockIndex *pindex,
|
||||||
} else {
|
} else {
|
||||||
DecrementNoteWitnesses(pindex);
|
DecrementNoteWitnesses(pindex);
|
||||||
}
|
}
|
||||||
|
UpdateSaplingNullifierNoteMapForBlock(pblock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CWallet::SetBestChain(const CBlockLocator& loc)
|
void CWallet::SetBestChain(const CBlockLocator& loc)
|
||||||
|
@ -583,15 +584,28 @@ set<uint256> CWallet::GetConflicts(const uint256& txid) const
|
||||||
|
|
||||||
for (const JSDescription& jsdesc : wtx.vjoinsplit) {
|
for (const JSDescription& jsdesc : wtx.vjoinsplit) {
|
||||||
for (const uint256& nullifier : jsdesc.nullifiers) {
|
for (const uint256& nullifier : jsdesc.nullifiers) {
|
||||||
if (mapTxNullifiers.count(nullifier) <= 1) {
|
if (mapTxSproutNullifiers.count(nullifier) <= 1) {
|
||||||
continue; // No conflict if zero or one spends
|
continue; // No conflict if zero or one spends
|
||||||
}
|
}
|
||||||
range_n = mapTxNullifiers.equal_range(nullifier);
|
range_n = mapTxSproutNullifiers.equal_range(nullifier);
|
||||||
for (TxNullifiers::const_iterator it = range_n.first; it != range_n.second; ++it) {
|
for (TxNullifiers::const_iterator it = range_n.first; it != range_n.second; ++it) {
|
||||||
result.insert(it->second);
|
result.insert(it->second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<TxNullifiers::const_iterator, TxNullifiers::const_iterator> range_o;
|
||||||
|
|
||||||
|
for (const SpendDescription &spend : wtx.vShieldedSpend) {
|
||||||
|
uint256 nullifier = spend.nullifier;
|
||||||
|
if (mapTxSaplingNullifiers.count(nullifier) <= 1) {
|
||||||
|
continue; // No conflict if zero or one spends
|
||||||
|
}
|
||||||
|
range_o = mapTxSaplingNullifiers.equal_range(nullifier);
|
||||||
|
for (TxNullifiers::const_iterator it = range_o.first; it != range_o.second; ++it) {
|
||||||
|
result.insert(it->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -673,7 +687,7 @@ void CWallet::SyncMetaData(pair<typename TxSpendMap<T>::iterator, typename TxSpe
|
||||||
CWalletTx* copyTo = &mapWallet[hash];
|
CWalletTx* copyTo = &mapWallet[hash];
|
||||||
if (copyFrom == copyTo) continue;
|
if (copyFrom == copyTo) continue;
|
||||||
copyTo->mapValue = copyFrom->mapValue;
|
copyTo->mapValue = copyFrom->mapValue;
|
||||||
// mapSproutNoteData not copied on purpose
|
// mapSproutNoteData and mapSaplingNoteData not copied on purpose
|
||||||
// (it is always set correctly for each CWalletTx)
|
// (it is always set correctly for each CWalletTx)
|
||||||
copyTo->vOrderForm = copyFrom->vOrderForm;
|
copyTo->vOrderForm = copyFrom->vOrderForm;
|
||||||
// fTimeReceivedIsTxTime not copied on purpose
|
// fTimeReceivedIsTxTime not copied on purpose
|
||||||
|
@ -710,10 +724,9 @@ bool CWallet::IsSpent(const uint256& hash, unsigned int n) const
|
||||||
* Note is spent if any non-conflicted transaction
|
* Note is spent if any non-conflicted transaction
|
||||||
* spends it:
|
* spends it:
|
||||||
*/
|
*/
|
||||||
bool CWallet::IsSpent(const uint256& nullifier) const
|
bool CWallet::IsSproutSpent(const uint256& nullifier) const {
|
||||||
{
|
|
||||||
pair<TxNullifiers::const_iterator, TxNullifiers::const_iterator> range;
|
pair<TxNullifiers::const_iterator, TxNullifiers::const_iterator> range;
|
||||||
range = mapTxNullifiers.equal_range(nullifier);
|
range = mapTxSproutNullifiers.equal_range(nullifier);
|
||||||
|
|
||||||
for (TxNullifiers::const_iterator it = range.first; it != range.second; ++it) {
|
for (TxNullifiers::const_iterator it = range.first; it != range.second; ++it) {
|
||||||
const uint256& wtxid = it->second;
|
const uint256& wtxid = it->second;
|
||||||
|
@ -725,7 +738,21 @@ bool CWallet::IsSpent(const uint256& nullifier) const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CWallet::AddToSpends(const COutPoint& outpoint, const uint256& wtxid)
|
bool CWallet::IsSaplingSpent(const uint256& nullifier) const {
|
||||||
|
pair<TxNullifiers::const_iterator, TxNullifiers::const_iterator> range;
|
||||||
|
range = mapTxSaplingNullifiers.equal_range(nullifier);
|
||||||
|
|
||||||
|
for (TxNullifiers::const_iterator it = range.first; it != range.second; ++it) {
|
||||||
|
const uint256& wtxid = it->second;
|
||||||
|
std::map<uint256, CWalletTx>::const_iterator mit = mapWallet.find(wtxid);
|
||||||
|
if (mit != mapWallet.end() && mit->second.GetDepthInMainChain() >= 0) {
|
||||||
|
return true; // Spent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CWallet::AddToTransparentSpends(const COutPoint& outpoint, const uint256& wtxid)
|
||||||
{
|
{
|
||||||
mapTxSpends.insert(make_pair(outpoint, wtxid));
|
mapTxSpends.insert(make_pair(outpoint, wtxid));
|
||||||
|
|
||||||
|
@ -734,12 +761,21 @@ void CWallet::AddToSpends(const COutPoint& outpoint, const uint256& wtxid)
|
||||||
SyncMetaData<COutPoint>(range);
|
SyncMetaData<COutPoint>(range);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CWallet::AddToSpends(const uint256& nullifier, const uint256& wtxid)
|
void CWallet::AddToSproutSpends(const uint256& nullifier, const uint256& wtxid)
|
||||||
{
|
{
|
||||||
mapTxNullifiers.insert(make_pair(nullifier, wtxid));
|
mapTxSproutNullifiers.insert(make_pair(nullifier, wtxid));
|
||||||
|
|
||||||
pair<TxNullifiers::iterator, TxNullifiers::iterator> range;
|
pair<TxNullifiers::iterator, TxNullifiers::iterator> range;
|
||||||
range = mapTxNullifiers.equal_range(nullifier);
|
range = mapTxSproutNullifiers.equal_range(nullifier);
|
||||||
|
SyncMetaData<uint256>(range);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CWallet::AddToSaplingSpends(const uint256& nullifier, const uint256& wtxid)
|
||||||
|
{
|
||||||
|
mapTxSaplingNullifiers.insert(make_pair(nullifier, wtxid));
|
||||||
|
|
||||||
|
pair<TxNullifiers::iterator, TxNullifiers::iterator> range;
|
||||||
|
range = mapTxSaplingNullifiers.equal_range(nullifier);
|
||||||
SyncMetaData<uint256>(range);
|
SyncMetaData<uint256>(range);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -751,13 +787,16 @@ void CWallet::AddToSpends(const uint256& wtxid)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (const CTxIn& txin : thisTx.vin) {
|
for (const CTxIn& txin : thisTx.vin) {
|
||||||
AddToSpends(txin.prevout, wtxid);
|
AddToTransparentSpends(txin.prevout, wtxid);
|
||||||
}
|
}
|
||||||
for (const JSDescription& jsdesc : thisTx.vjoinsplit) {
|
for (const JSDescription& jsdesc : thisTx.vjoinsplit) {
|
||||||
for (const uint256& nullifier : jsdesc.nullifiers) {
|
for (const uint256& nullifier : jsdesc.nullifiers) {
|
||||||
AddToSpends(nullifier, wtxid);
|
AddToSproutSpends(nullifier, wtxid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (const SpendDescription &spend : thisTx.vShieldedSpend) {
|
||||||
|
AddToSaplingSpends(spend.nullifier, wtxid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CWallet::ClearNoteWitnessCache()
|
void CWallet::ClearNoteWitnessCache()
|
||||||
|
@ -1148,7 +1187,7 @@ bool CWallet::UpdateNullifierNoteMap()
|
||||||
auto i = item.first.js;
|
auto i = item.first.js;
|
||||||
auto hSig = wtxItem.second.vjoinsplit[i].h_sig(
|
auto hSig = wtxItem.second.vjoinsplit[i].h_sig(
|
||||||
*pzcashParams, wtxItem.second.joinSplitPubKey);
|
*pzcashParams, wtxItem.second.joinSplitPubKey);
|
||||||
item.second.nullifier = GetNoteNullifier(
|
item.second.nullifier = GetSproutNoteNullifier(
|
||||||
wtxItem.second.vjoinsplit[i],
|
wtxItem.second.vjoinsplit[i],
|
||||||
item.second.address,
|
item.second.address,
|
||||||
dec,
|
dec,
|
||||||
|
@ -1157,6 +1196,10 @@ bool CWallet::UpdateNullifierNoteMap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Sapling. This method is only called from RPC walletpassphrase, which is currently unsupported
|
||||||
|
// as RPC encryptwallet is hidden behind two flags: -developerencryptwallet -experimentalfeatures
|
||||||
|
|
||||||
UpdateNullifierNoteMapWithTx(wtxItem.second);
|
UpdateNullifierNoteMapWithTx(wtxItem.second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1164,7 +1207,8 @@ bool CWallet::UpdateNullifierNoteMap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update mapNullifiersToNotes with the cached nullifiers in this tx.
|
* Update mapSproutNullifiersToNotes and mapSaplingNullifiersToNotes
|
||||||
|
* with the cached nullifiers in this tx.
|
||||||
*/
|
*/
|
||||||
void CWallet::UpdateNullifierNoteMapWithTx(const CWalletTx& wtx)
|
void CWallet::UpdateNullifierNoteMapWithTx(const CWalletTx& wtx)
|
||||||
{
|
{
|
||||||
|
@ -1172,9 +1216,74 @@ void CWallet::UpdateNullifierNoteMapWithTx(const CWalletTx& wtx)
|
||||||
LOCK(cs_wallet);
|
LOCK(cs_wallet);
|
||||||
for (const mapSproutNoteData_t::value_type& item : wtx.mapSproutNoteData) {
|
for (const mapSproutNoteData_t::value_type& item : wtx.mapSproutNoteData) {
|
||||||
if (item.second.nullifier) {
|
if (item.second.nullifier) {
|
||||||
mapNullifiersToNotes[*item.second.nullifier] = item.first;
|
mapSproutNullifiersToNotes[*item.second.nullifier] = item.first;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const mapSaplingNoteData_t::value_type& item : wtx.mapSaplingNoteData) {
|
||||||
|
if (item.second.nullifier) {
|
||||||
|
mapSaplingNullifiersToNotes[*item.second.nullifier] = item.first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update mapSaplingNullifiersToNotes, computing the nullifier from a cached witness if necessary.
|
||||||
|
*/
|
||||||
|
void CWallet::UpdateSaplingNullifierNoteMapWithTx(CWalletTx& wtx) {
|
||||||
|
LOCK(cs_wallet);
|
||||||
|
|
||||||
|
for (mapSaplingNoteData_t::value_type &item : wtx.mapSaplingNoteData) {
|
||||||
|
SaplingOutPoint op = item.first;
|
||||||
|
SaplingNoteData nd = item.second;
|
||||||
|
|
||||||
|
if (nd.witnesses.empty()) {
|
||||||
|
// If there are no witnesses, erase the nullifier and associated mapping.
|
||||||
|
if (item.second.nullifier) {
|
||||||
|
mapSaplingNullifiersToNotes.erase(item.second.nullifier.get());
|
||||||
|
}
|
||||||
|
item.second.nullifier = boost::none;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
uint64_t position = nd.witnesses.front().position();
|
||||||
|
SaplingFullViewingKey fvk = mapSaplingFullViewingKeys.at(nd.ivk);
|
||||||
|
OutputDescription output = wtx.vShieldedOutput[op.n];
|
||||||
|
auto optPlaintext = SaplingNotePlaintext::decrypt(output.encCiphertext, nd.ivk, output.ephemeralKey, output.cm);
|
||||||
|
if (!optPlaintext) {
|
||||||
|
// An item in mapSaplingNoteData must have already been successfully decrypted,
|
||||||
|
// otherwise the item would not exist in the first place.
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
auto optNote = optPlaintext.get().note(nd.ivk);
|
||||||
|
if (!optNote) {
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
auto optNullifier = optNote.get().nullifier(fvk, position);
|
||||||
|
if (!optNullifier) {
|
||||||
|
// This should not happen. If it does, maybe the position has been corrupted or miscalculated?
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
uint256 nullifier = optNullifier.get();
|
||||||
|
mapSaplingNullifiersToNotes[nullifier] = op;
|
||||||
|
item.second.nullifier = nullifier;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterate over transactions in a block and update the cached Sapling nullifiers
|
||||||
|
* for transactions which belong to the wallet.
|
||||||
|
*/
|
||||||
|
void CWallet::UpdateSaplingNullifierNoteMapForBlock(const CBlock *pblock) {
|
||||||
|
LOCK(cs_wallet);
|
||||||
|
|
||||||
|
for (const CTransaction& tx : pblock->vtx) {
|
||||||
|
auto hash = tx.GetHash();
|
||||||
|
bool txIsOurs = mapWallet.count(hash);
|
||||||
|
if (txIsOurs) {
|
||||||
|
UpdateSaplingNullifierNoteMapWithTx(mapWallet[hash]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1305,21 +1414,39 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletD
|
||||||
|
|
||||||
bool CWallet::UpdatedNoteData(const CWalletTx& wtxIn, CWalletTx& wtx)
|
bool CWallet::UpdatedNoteData(const CWalletTx& wtxIn, CWalletTx& wtx)
|
||||||
{
|
{
|
||||||
if (wtxIn.mapSproutNoteData.empty() || wtxIn.mapSproutNoteData == wtx.mapSproutNoteData) {
|
bool unchangedSproutFlag = (wtxIn.mapSproutNoteData.empty() || wtxIn.mapSproutNoteData == wtx.mapSproutNoteData);
|
||||||
return false;
|
if (!unchangedSproutFlag) {
|
||||||
}
|
auto tmp = wtxIn.mapSproutNoteData;
|
||||||
auto tmp = wtxIn.mapSproutNoteData;
|
// Ensure we keep any cached witnesses we may already have
|
||||||
// Ensure we keep any cached witnesses we may already have
|
for (const std::pair <JSOutPoint, SproutNoteData> nd : wtx.mapSproutNoteData) {
|
||||||
for (const std::pair<JSOutPoint, SproutNoteData> nd : wtx.mapSproutNoteData) {
|
if (tmp.count(nd.first) && nd.second.witnesses.size() > 0) {
|
||||||
if (tmp.count(nd.first) && nd.second.witnesses.size() > 0) {
|
tmp.at(nd.first).witnesses.assign(
|
||||||
tmp.at(nd.first).witnesses.assign(
|
nd.second.witnesses.cbegin(), nd.second.witnesses.cend());
|
||||||
nd.second.witnesses.cbegin(), nd.second.witnesses.cend());
|
}
|
||||||
|
tmp.at(nd.first).witnessHeight = nd.second.witnessHeight;
|
||||||
}
|
}
|
||||||
tmp.at(nd.first).witnessHeight = nd.second.witnessHeight;
|
// Now copy over the updated note data
|
||||||
|
wtx.mapSproutNoteData = tmp;
|
||||||
}
|
}
|
||||||
// Now copy over the updated note data
|
|
||||||
wtx.mapSproutNoteData = tmp;
|
bool unchangedSaplingFlag = (wtxIn.mapSaplingNoteData.empty() || wtxIn.mapSaplingNoteData == wtx.mapSaplingNoteData);
|
||||||
return true;
|
if (!unchangedSaplingFlag) {
|
||||||
|
auto tmp = wtxIn.mapSaplingNoteData;
|
||||||
|
// Ensure we keep any cached witnesses we may already have
|
||||||
|
|
||||||
|
for (const std::pair <SaplingOutPoint, SaplingNoteData> nd : wtx.mapSaplingNoteData) {
|
||||||
|
if (tmp.count(nd.first) && nd.second.witnesses.size() > 0) {
|
||||||
|
tmp.at(nd.first).witnesses.assign(
|
||||||
|
nd.second.witnesses.cbegin(), nd.second.witnesses.cend());
|
||||||
|
}
|
||||||
|
tmp.at(nd.first).witnessHeight = nd.second.witnessHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now copy over the updated note data
|
||||||
|
wtx.mapSaplingNoteData = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !unchangedSproutFlag || !unchangedSaplingFlag;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1333,15 +1460,19 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl
|
||||||
AssertLockHeld(cs_wallet);
|
AssertLockHeld(cs_wallet);
|
||||||
bool fExisted = mapWallet.count(tx.GetHash()) != 0;
|
bool fExisted = mapWallet.count(tx.GetHash()) != 0;
|
||||||
if (fExisted && !fUpdate) return false;
|
if (fExisted && !fUpdate) return false;
|
||||||
auto noteData = FindMyNotes(tx);
|
auto sproutNoteData = FindMySproutNotes(tx);
|
||||||
if (fExisted || IsMine(tx) || IsFromMe(tx) || noteData.size() > 0)
|
auto saplingNoteData = FindMySaplingNotes(tx);
|
||||||
|
if (fExisted || IsMine(tx) || IsFromMe(tx) || sproutNoteData.size() > 0 || saplingNoteData.size() > 0)
|
||||||
{
|
{
|
||||||
CWalletTx wtx(this,tx);
|
CWalletTx wtx(this,tx);
|
||||||
|
|
||||||
if (noteData.size() > 0) {
|
if (sproutNoteData.size() > 0) {
|
||||||
wtx.SetSproutNoteData(noteData);
|
wtx.SetSproutNoteData(sproutNoteData);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (saplingNoteData.size() > 0) {
|
||||||
|
wtx.SetSaplingNoteData(saplingNoteData);
|
||||||
}
|
}
|
||||||
// TODO: Sapling note data
|
|
||||||
|
|
||||||
// Get merkle branch if transaction was found in a block
|
// Get merkle branch if transaction was found in a block
|
||||||
if (pblock)
|
if (pblock)
|
||||||
|
@ -1378,12 +1509,20 @@ void CWallet::MarkAffectedTransactionsDirty(const CTransaction& tx)
|
||||||
}
|
}
|
||||||
for (const JSDescription& jsdesc : tx.vjoinsplit) {
|
for (const JSDescription& jsdesc : tx.vjoinsplit) {
|
||||||
for (const uint256& nullifier : jsdesc.nullifiers) {
|
for (const uint256& nullifier : jsdesc.nullifiers) {
|
||||||
if (mapNullifiersToNotes.count(nullifier) &&
|
if (mapSproutNullifiersToNotes.count(nullifier) &&
|
||||||
mapWallet.count(mapNullifiersToNotes[nullifier].hash)) {
|
mapWallet.count(mapSproutNullifiersToNotes[nullifier].hash)) {
|
||||||
mapWallet[mapNullifiersToNotes[nullifier].hash].MarkDirty();
|
mapWallet[mapSproutNullifiersToNotes[nullifier].hash].MarkDirty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const SpendDescription &spend : tx.vShieldedSpend) {
|
||||||
|
uint256 nullifier = spend.nullifier;
|
||||||
|
if (mapSaplingNullifiersToNotes.count(nullifier) &&
|
||||||
|
mapWallet.count(mapSaplingNullifiersToNotes[nullifier].hash)) {
|
||||||
|
mapWallet[mapSaplingNullifiersToNotes[nullifier].hash].MarkDirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CWallet::EraseFromWallet(const uint256 &hash)
|
void CWallet::EraseFromWallet(const uint256 &hash)
|
||||||
|
@ -1403,11 +1542,11 @@ void CWallet::EraseFromWallet(const uint256 &hash)
|
||||||
* Returns a nullifier if the SpendingKey is available
|
* Returns a nullifier if the SpendingKey is available
|
||||||
* Throws std::runtime_error if the decryptor doesn't match this note
|
* Throws std::runtime_error if the decryptor doesn't match this note
|
||||||
*/
|
*/
|
||||||
boost::optional<uint256> CWallet::GetNoteNullifier(const JSDescription& jsdesc,
|
boost::optional<uint256> CWallet::GetSproutNoteNullifier(const JSDescription &jsdesc,
|
||||||
const libzcash::SproutPaymentAddress& address,
|
const libzcash::SproutPaymentAddress &address,
|
||||||
const ZCNoteDecryption& dec,
|
const ZCNoteDecryption &dec,
|
||||||
const uint256& hSig,
|
const uint256 &hSig,
|
||||||
uint8_t n) const
|
uint8_t n) const
|
||||||
{
|
{
|
||||||
boost::optional<uint256> ret;
|
boost::optional<uint256> ret;
|
||||||
auto note_pt = libzcash::SproutNotePlaintext::decrypt(
|
auto note_pt = libzcash::SproutNotePlaintext::decrypt(
|
||||||
|
@ -1432,10 +1571,10 @@ boost::optional<uint256> CWallet::GetNoteNullifier(const JSDescription& jsdesc,
|
||||||
* PaymentAddresses in this wallet.
|
* PaymentAddresses in this wallet.
|
||||||
*
|
*
|
||||||
* It should never be necessary to call this method with a CWalletTx, because
|
* It should never be necessary to call this method with a CWalletTx, because
|
||||||
* the result of FindMyNotes (for the addresses available at the time) will
|
* the result of FindMySproutNotes (for the addresses available at the time) will
|
||||||
* already have been cached in CWalletTx.mapSproutNoteData.
|
* already have been cached in CWalletTx.mapSproutNoteData.
|
||||||
*/
|
*/
|
||||||
mapSproutNoteData_t CWallet::FindMyNotes(const CTransaction& tx) const
|
mapSproutNoteData_t CWallet::FindMySproutNotes(const CTransaction &tx) const
|
||||||
{
|
{
|
||||||
LOCK(cs_SpendingKeyStore);
|
LOCK(cs_SpendingKeyStore);
|
||||||
uint256 hash = tx.GetHash();
|
uint256 hash = tx.GetHash();
|
||||||
|
@ -1448,7 +1587,7 @@ mapSproutNoteData_t CWallet::FindMyNotes(const CTransaction& tx) const
|
||||||
try {
|
try {
|
||||||
auto address = item.first;
|
auto address = item.first;
|
||||||
JSOutPoint jsoutpt {hash, i, j};
|
JSOutPoint jsoutpt {hash, i, j};
|
||||||
auto nullifier = GetNoteNullifier(
|
auto nullifier = GetSproutNoteNullifier(
|
||||||
tx.vjoinsplit[i],
|
tx.vjoinsplit[i],
|
||||||
address,
|
address,
|
||||||
item.second,
|
item.second,
|
||||||
|
@ -1465,7 +1604,7 @@ mapSproutNoteData_t CWallet::FindMyNotes(const CTransaction& tx) const
|
||||||
// Couldn't decrypt with this decryptor
|
// Couldn't decrypt with this decryptor
|
||||||
} catch (const std::exception &exc) {
|
} catch (const std::exception &exc) {
|
||||||
// Unexpected failure
|
// Unexpected failure
|
||||||
LogPrintf("FindMyNotes(): Unexpected error while testing decrypt:\n");
|
LogPrintf("FindMySproutNotes(): Unexpected error while testing decrypt:\n");
|
||||||
LogPrintf("%s\n", exc.what());
|
LogPrintf("%s\n", exc.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1474,12 +1613,62 @@ mapSproutNoteData_t CWallet::FindMyNotes(const CTransaction& tx) const
|
||||||
return noteData;
|
return noteData;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CWallet::IsFromMe(const uint256& nullifier) const
|
|
||||||
|
/**
|
||||||
|
* Finds all output notes in the given transaction that have been sent to
|
||||||
|
* SaplingPaymentAddresses in this wallet.
|
||||||
|
*
|
||||||
|
* It should never be necessary to call this method with a CWalletTx, because
|
||||||
|
* 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
|
||||||
|
{
|
||||||
|
LOCK(cs_SpendingKeyStore);
|
||||||
|
uint256 hash = tx.GetHash();
|
||||||
|
|
||||||
|
mapSaplingNoteData_t noteData;
|
||||||
|
|
||||||
|
// Protocol Spec: 4.19 Block Chain Scanning (Sapling)
|
||||||
|
for (uint32_t i = 0; i < tx.vShieldedOutput.size(); ++i) {
|
||||||
|
const OutputDescription output = tx.vShieldedOutput[i];
|
||||||
|
for (auto it = mapSaplingFullViewingKeys.begin(); it != mapSaplingFullViewingKeys.end(); ++it) {
|
||||||
|
SaplingIncomingViewingKey ivk = it->first;
|
||||||
|
auto result = SaplingNotePlaintext::decrypt(output.encCiphertext, ivk, output.ephemeralKey, output.cm);
|
||||||
|
if (!result) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 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};
|
||||||
|
SaplingNoteData nd;
|
||||||
|
nd.ivk = ivk;
|
||||||
|
noteData.insert(std::make_pair(op, nd));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return noteData;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CWallet::IsSproutNullifierFromMe(const uint256& nullifier) const
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
LOCK(cs_wallet);
|
LOCK(cs_wallet);
|
||||||
if (mapNullifiersToNotes.count(nullifier) &&
|
if (mapSproutNullifiersToNotes.count(nullifier) &&
|
||||||
mapWallet.count(mapNullifiersToNotes.at(nullifier).hash)) {
|
mapWallet.count(mapSproutNullifiersToNotes.at(nullifier).hash)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CWallet::IsSaplingNullifierFromMe(const uint256& nullifier) const
|
||||||
|
{
|
||||||
|
{
|
||||||
|
LOCK(cs_wallet);
|
||||||
|
if (mapSaplingNullifiersToNotes.count(nullifier) &&
|
||||||
|
mapWallet.count(mapSaplingNullifiersToNotes.at(nullifier).hash)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1627,11 +1816,16 @@ bool CWallet::IsFromMe(const CTransaction& tx) const
|
||||||
}
|
}
|
||||||
for (const JSDescription& jsdesc : tx.vjoinsplit) {
|
for (const JSDescription& jsdesc : tx.vjoinsplit) {
|
||||||
for (const uint256& nullifier : jsdesc.nullifiers) {
|
for (const uint256& nullifier : jsdesc.nullifiers) {
|
||||||
if (IsFromMe(nullifier)) {
|
if (IsSproutNullifierFromMe(nullifier)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (const SpendDescription &spend : tx.vShieldedSpend) {
|
||||||
|
if (IsSaplingNullifierFromMe(spend.nullifier)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1680,7 +1874,7 @@ void CWalletTx::SetSproutNoteData(mapSproutNoteData_t ¬eData)
|
||||||
// Store the address and nullifier for the Note
|
// Store the address and nullifier for the Note
|
||||||
mapSproutNoteData[nd.first] = nd.second;
|
mapSproutNoteData[nd.first] = nd.second;
|
||||||
} else {
|
} else {
|
||||||
// If FindMyNotes() was used to obtain noteData,
|
// If FindMySproutNotes() was used to obtain noteData,
|
||||||
// this should never happen
|
// this should never happen
|
||||||
throw std::logic_error("CWalletTx::SetSproutNoteData(): Invalid note");
|
throw std::logic_error("CWalletTx::SetSproutNoteData(): Invalid note");
|
||||||
}
|
}
|
||||||
|
@ -1757,25 +1951,10 @@ void CWalletTx::GetAmounts(list<COutputEntry>& listReceived,
|
||||||
CAmount nDebit = GetDebit(filter);
|
CAmount nDebit = GetDebit(filter);
|
||||||
bool isFromMyTaddr = nDebit > 0; // debit>0 means we signed/sent this transaction
|
bool isFromMyTaddr = nDebit > 0; // debit>0 means we signed/sent this transaction
|
||||||
|
|
||||||
// Does this tx spend my notes?
|
|
||||||
bool isFromMyZaddr = false;
|
|
||||||
for (const JSDescription& js : vjoinsplit) {
|
|
||||||
for (const uint256& nullifier : js.nullifiers) {
|
|
||||||
if (pwallet->IsFromMe(nullifier)) {
|
|
||||||
isFromMyZaddr = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isFromMyZaddr) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute fee if we sent this transaction.
|
// Compute fee if we sent this transaction.
|
||||||
if (isFromMyTaddr) {
|
if (isFromMyTaddr) {
|
||||||
CAmount nValueOut = GetValueOut(); // transparent outputs plus all vpub_old
|
CAmount nValueOut = GetValueOut(); // transparent outputs plus all Sprout vpub_old and negative Sapling valueBalance
|
||||||
CAmount nValueIn = 0;
|
CAmount nValueIn = GetShieldedValueIn();
|
||||||
nValueIn += GetShieldedValueIn();
|
|
||||||
nFee = nDebit - nValueOut + nValueIn;
|
nFee = nDebit - nValueOut + nValueIn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1788,7 +1967,7 @@ void CWalletTx::GetAmounts(list<COutputEntry>& listReceived,
|
||||||
|
|
||||||
// Check input side
|
// Check input side
|
||||||
for (const uint256& nullifier : js.nullifiers) {
|
for (const uint256& nullifier : js.nullifiers) {
|
||||||
if (pwallet->IsFromMe(nullifier)) {
|
if (pwallet->IsSproutNullifierFromMe(nullifier)) {
|
||||||
fMyJSDesc = true;
|
fMyJSDesc = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1824,6 +2003,18 @@ void CWalletTx::GetAmounts(list<COutputEntry>& listReceived,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we sent utxos from this transaction, create output for value taken from (negative valueBalance)
|
||||||
|
// or added (positive valueBalance) to the transparent value pool by Sapling shielding and unshielding.
|
||||||
|
if (isFromMyTaddr) {
|
||||||
|
if (valueBalance < 0) {
|
||||||
|
COutputEntry output = {CNoDestination(), -valueBalance, (int) vout.size()};
|
||||||
|
listSent.push_back(output);
|
||||||
|
} else if (valueBalance > 0) {
|
||||||
|
COutputEntry output = {CNoDestination(), valueBalance, (int) vout.size()};
|
||||||
|
listReceived.push_back(output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Sent/received.
|
// Sent/received.
|
||||||
for (unsigned int i = 0; i < vout.size(); ++i)
|
for (unsigned int i = 0; i < vout.size(); ++i)
|
||||||
{
|
{
|
||||||
|
@ -3947,7 +4138,7 @@ void CWallet::GetFilteredNotes(
|
||||||
}
|
}
|
||||||
|
|
||||||
// skip note which has been spent
|
// skip note which has been spent
|
||||||
if (ignoreSpent && nd.nullifier && IsSpent(*nd.nullifier)) {
|
if (ignoreSpent && nd.nullifier && IsSproutSpent(*nd.nullifier)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4028,7 +4219,7 @@ void CWallet::GetUnspentFilteredNotes(
|
||||||
}
|
}
|
||||||
|
|
||||||
// skip note which has been spent
|
// skip note which has been spent
|
||||||
if (nd.nullifier && IsSpent(*nd.nullifier)) {
|
if (nd.nullifier && IsSproutSpent(*nd.nullifier)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -225,7 +225,7 @@ public:
|
||||||
/**
|
/**
|
||||||
* Block height corresponding to the most current witness.
|
* Block height corresponding to the most current witness.
|
||||||
*
|
*
|
||||||
* When we first create a SproutNoteData in CWallet::FindMyNotes, this is set to
|
* When we first create a SproutNoteData in CWallet::FindMySproutNotes, this is set to
|
||||||
* -1 as a placeholder. The next time CWallet::ChainTip is called, we can
|
* -1 as a placeholder. The next time CWallet::ChainTip is called, we can
|
||||||
* determine what height the witness cache for this note is valid for (even
|
* determine what height the witness cache for this note is valid for (even
|
||||||
* if no witnesses were cached), and so can set the correct value in
|
* if no witnesses were cached), and so can set the correct value in
|
||||||
|
@ -267,13 +267,25 @@ class SaplingNoteData
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* We initialize the hight to -1 for the same reason as we do in SproutNoteData.
|
* We initialize the height to -1 for the same reason as we do in SproutNoteData.
|
||||||
* See the comment in that class for a full description.
|
* See the comment in that class for a full description.
|
||||||
*/
|
*/
|
||||||
SaplingNoteData() : witnessHeight {-1} { }
|
SaplingNoteData() : witnessHeight {-1}, nullifier() { }
|
||||||
|
SaplingNoteData(libzcash::SaplingIncomingViewingKey ivk) : ivk {ivk}, witnessHeight {-1}, nullifier() { }
|
||||||
|
SaplingNoteData(libzcash::SaplingIncomingViewingKey ivk, uint256 n) : ivk {ivk}, witnessHeight {-1}, nullifier(n) { }
|
||||||
|
|
||||||
std::list<SaplingWitness> witnesses;
|
std::list<SaplingWitness> witnesses;
|
||||||
int witnessHeight;
|
int witnessHeight;
|
||||||
|
libzcash::SaplingIncomingViewingKey ivk;
|
||||||
|
boost::optional<uint256> nullifier;
|
||||||
|
|
||||||
|
friend bool operator==(const SaplingNoteData& a, const SaplingNoteData& b) {
|
||||||
|
return (a.ivk == b.ivk && a.nullifier == b.nullifier && a.witnessHeight == b.witnessHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator!=(const SaplingNoteData& a, const SaplingNoteData& b) {
|
||||||
|
return !(a == b);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::map<JSOutPoint, SproutNoteData> mapSproutNoteData_t;
|
typedef std::map<JSOutPoint, SproutNoteData> mapSproutNoteData_t;
|
||||||
|
@ -715,10 +727,12 @@ private:
|
||||||
* detect and report conflicts (double-spends).
|
* detect and report conflicts (double-spends).
|
||||||
*/
|
*/
|
||||||
typedef TxSpendMap<uint256> TxNullifiers;
|
typedef TxSpendMap<uint256> TxNullifiers;
|
||||||
TxNullifiers mapTxNullifiers;
|
TxNullifiers mapTxSproutNullifiers;
|
||||||
|
TxNullifiers mapTxSaplingNullifiers;
|
||||||
|
|
||||||
void AddToSpends(const COutPoint& outpoint, const uint256& wtxid);
|
void AddToTransparentSpends(const COutPoint& outpoint, const uint256& wtxid);
|
||||||
void AddToSpends(const uint256& nullifier, const uint256& wtxid);
|
void AddToSproutSpends(const uint256& nullifier, const uint256& wtxid);
|
||||||
|
void AddToSaplingSpends(const uint256& nullifier, const uint256& wtxid);
|
||||||
void AddToSpends(const uint256& wtxid);
|
void AddToSpends(const uint256& wtxid);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -895,7 +909,9 @@ public:
|
||||||
* - Restarting the node with -reindex (which operates on a locked wallet
|
* - Restarting the node with -reindex (which operates on a locked wallet
|
||||||
* but with the now-cached nullifiers).
|
* but with the now-cached nullifiers).
|
||||||
*/
|
*/
|
||||||
std::map<uint256, JSOutPoint> mapNullifiersToNotes;
|
std::map<uint256, JSOutPoint> mapSproutNullifiersToNotes;
|
||||||
|
|
||||||
|
std::map<uint256, SaplingOutPoint> mapSaplingNullifiersToNotes;
|
||||||
|
|
||||||
std::map<uint256, CWalletTx> mapWallet;
|
std::map<uint256, CWalletTx> mapWallet;
|
||||||
|
|
||||||
|
@ -920,7 +936,8 @@ public:
|
||||||
bool SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, std::vector<COutput> vCoins, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet) const;
|
bool SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, std::vector<COutput> vCoins, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet) const;
|
||||||
|
|
||||||
bool IsSpent(const uint256& hash, unsigned int n) const;
|
bool IsSpent(const uint256& hash, unsigned int n) const;
|
||||||
bool IsSpent(const uint256& nullifier) const;
|
bool IsSproutSpent(const uint256& nullifier) const;
|
||||||
|
bool IsSaplingSpent(const uint256& nullifier) const;
|
||||||
|
|
||||||
bool IsLockedCoin(uint256 hash, unsigned int n) const;
|
bool IsLockedCoin(uint256 hash, unsigned int n) const;
|
||||||
void LockCoin(COutPoint& output);
|
void LockCoin(COutPoint& output);
|
||||||
|
@ -1036,6 +1053,8 @@ public:
|
||||||
void MarkDirty();
|
void MarkDirty();
|
||||||
bool UpdateNullifierNoteMap();
|
bool UpdateNullifierNoteMap();
|
||||||
void UpdateNullifierNoteMapWithTx(const CWalletTx& wtx);
|
void UpdateNullifierNoteMapWithTx(const CWalletTx& wtx);
|
||||||
|
void UpdateSaplingNullifierNoteMapWithTx(CWalletTx& wtx);
|
||||||
|
void UpdateSaplingNullifierNoteMapForBlock(const CBlock* pblock);
|
||||||
bool AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletDB* pwalletdb);
|
bool AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletDB* pwalletdb);
|
||||||
void SyncTransaction(const CTransaction& tx, const CBlock* pblock);
|
void SyncTransaction(const CTransaction& tx, const CBlock* pblock);
|
||||||
bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate);
|
bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate);
|
||||||
|
@ -1076,14 +1095,17 @@ public:
|
||||||
|
|
||||||
std::set<CTxDestination> GetAccountAddresses(const std::string& strAccount) const;
|
std::set<CTxDestination> GetAccountAddresses(const std::string& strAccount) const;
|
||||||
|
|
||||||
boost::optional<uint256> GetNoteNullifier(
|
boost::optional<uint256> GetSproutNoteNullifier(
|
||||||
const JSDescription& jsdesc,
|
const JSDescription& jsdesc,
|
||||||
const libzcash::SproutPaymentAddress& address,
|
const libzcash::SproutPaymentAddress& address,
|
||||||
const ZCNoteDecryption& dec,
|
const ZCNoteDecryption& dec,
|
||||||
const uint256& hSig,
|
const uint256& hSig,
|
||||||
uint8_t n) const;
|
uint8_t n) const;
|
||||||
mapSproutNoteData_t FindMyNotes(const CTransaction& tx) const;
|
mapSproutNoteData_t FindMySproutNotes(const CTransaction& tx) const;
|
||||||
bool IsFromMe(const uint256& nullifier) const;
|
mapSaplingNoteData_t FindMySaplingNotes(const CTransaction& tx) const;
|
||||||
|
bool IsSproutNullifierFromMe(const uint256& nullifier) const;
|
||||||
|
bool IsSaplingNullifierFromMe(const uint256& nullifier) const;
|
||||||
|
|
||||||
void GetSproutNoteWitnesses(
|
void GetSproutNoteWitnesses(
|
||||||
std::vector<JSOutPoint> notes,
|
std::vector<JSOutPoint> notes,
|
||||||
std::vector<boost::optional<SproutWitness>>& witnesses,
|
std::vector<boost::optional<SproutWitness>>& witnesses,
|
||||||
|
|
|
@ -187,7 +187,8 @@ boost::optional<SaplingOutgoingPlaintext> SaplingOutgoingPlaintext::decrypt(
|
||||||
boost::optional<SaplingNotePlaintext> SaplingNotePlaintext::decrypt(
|
boost::optional<SaplingNotePlaintext> SaplingNotePlaintext::decrypt(
|
||||||
const SaplingEncCiphertext &ciphertext,
|
const SaplingEncCiphertext &ciphertext,
|
||||||
const uint256 &ivk,
|
const uint256 &ivk,
|
||||||
const uint256 &epk
|
const uint256 &epk,
|
||||||
|
const uint256 &cmu
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
auto pt = AttemptSaplingEncDecryption(ciphertext, ivk, epk);
|
auto pt = AttemptSaplingEncDecryption(ciphertext, ivk, epk);
|
||||||
|
@ -204,6 +205,27 @@ boost::optional<SaplingNotePlaintext> SaplingNotePlaintext::decrypt(
|
||||||
|
|
||||||
assert(ss.size() == 0);
|
assert(ss.size() == 0);
|
||||||
|
|
||||||
|
uint256 pk_d;
|
||||||
|
if (!librustzcash_ivk_to_pkd(ivk.begin(), ret.d.data(), pk_d.begin())) {
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint256 cmu_expected;
|
||||||
|
if (!librustzcash_sapling_compute_cm(
|
||||||
|
ret.d.data(),
|
||||||
|
pk_d.begin(),
|
||||||
|
ret.value(),
|
||||||
|
ret.rcm.begin(),
|
||||||
|
cmu_expected.begin()
|
||||||
|
))
|
||||||
|
{
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmu_expected != cmu) {
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,7 +233,8 @@ boost::optional<SaplingNotePlaintext> SaplingNotePlaintext::decrypt(
|
||||||
const SaplingEncCiphertext &ciphertext,
|
const SaplingEncCiphertext &ciphertext,
|
||||||
const uint256 &epk,
|
const uint256 &epk,
|
||||||
const uint256 &esk,
|
const uint256 &esk,
|
||||||
const uint256 &pk_d
|
const uint256 &pk_d,
|
||||||
|
const uint256 &cmu
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
auto pt = AttemptSaplingEncDecryption(ciphertext, epk, esk, pk_d);
|
auto pt = AttemptSaplingEncDecryption(ciphertext, epk, esk, pk_d);
|
||||||
|
@ -226,6 +249,22 @@ boost::optional<SaplingNotePlaintext> SaplingNotePlaintext::decrypt(
|
||||||
SaplingNotePlaintext ret;
|
SaplingNotePlaintext ret;
|
||||||
ss >> ret;
|
ss >> ret;
|
||||||
|
|
||||||
|
uint256 cmu_expected;
|
||||||
|
if (!librustzcash_sapling_compute_cm(
|
||||||
|
ret.d.data(),
|
||||||
|
pk_d.begin(),
|
||||||
|
ret.value(),
|
||||||
|
ret.rcm.begin(),
|
||||||
|
cmu_expected.begin()
|
||||||
|
))
|
||||||
|
{
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmu_expected != cmu) {
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
|
||||||
assert(ss.size() == 0);
|
assert(ss.size() == 0);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -130,14 +130,16 @@ public:
|
||||||
static boost::optional<SaplingNotePlaintext> decrypt(
|
static boost::optional<SaplingNotePlaintext> decrypt(
|
||||||
const SaplingEncCiphertext &ciphertext,
|
const SaplingEncCiphertext &ciphertext,
|
||||||
const uint256 &ivk,
|
const uint256 &ivk,
|
||||||
const uint256 &epk
|
const uint256 &epk,
|
||||||
|
const uint256 &cmu
|
||||||
);
|
);
|
||||||
|
|
||||||
static boost::optional<SaplingNotePlaintext> decrypt(
|
static boost::optional<SaplingNotePlaintext> decrypt(
|
||||||
const SaplingEncCiphertext &ciphertext,
|
const SaplingEncCiphertext &ciphertext,
|
||||||
const uint256 &epk,
|
const uint256 &epk,
|
||||||
const uint256 &esk,
|
const uint256 &esk,
|
||||||
const uint256 &pk_d
|
const uint256 &pk_d,
|
||||||
|
const uint256 &cmu
|
||||||
);
|
);
|
||||||
|
|
||||||
boost::optional<SaplingNote> note(const SaplingIncomingViewingKey& ivk) const;
|
boost::optional<SaplingNote> note(const SaplingIncomingViewingKey& ivk) const;
|
||||||
|
|
|
@ -291,7 +291,7 @@ double benchmark_try_decrypt_notes(size_t nAddrs)
|
||||||
|
|
||||||
struct timeval tv_start;
|
struct timeval tv_start;
|
||||||
timer_start(tv_start);
|
timer_start(tv_start);
|
||||||
auto nd = wallet.FindMyNotes(tx);
|
auto nd = wallet.FindMySproutNotes(tx);
|
||||||
return timer_stop(tv_start);
|
return timer_stop(tv_start);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue