@@ -201,12 +201,18 @@ class CNoteData
|
||||
public:
|
||||
libzcash::PaymentAddress address;
|
||||
|
||||
// It's okay to cache the nullifier in the wallet, because we are storing
|
||||
// the spending key there too, which could be used to derive this.
|
||||
// If PR #1210 is merged, we need to revisit the threat model and decide
|
||||
// whether it is okay to store this unencrypted while the spending key is
|
||||
// encrypted.
|
||||
uint256 nullifier;
|
||||
/**
|
||||
* Cached note nullifier. May not be set if the wallet was not unlocked when
|
||||
* this was CNoteData was created. If not set, we always assume that the
|
||||
* note has not been spent.
|
||||
*
|
||||
* It's okay to cache the nullifier in the wallet, because we are storing
|
||||
* the spending key there too, which could be used to derive this.
|
||||
* If PR #1210 is merged, we need to revisit the threat model and decide
|
||||
* whether it is okay to store this unencrypted while the spending key is
|
||||
* encrypted.
|
||||
*/
|
||||
boost::optional<uint256> nullifier;
|
||||
|
||||
/**
|
||||
* Cached incremental witnesses for spendable Notes.
|
||||
@@ -215,6 +221,7 @@ public:
|
||||
std::list<ZCIncrementalWitness> witnesses;
|
||||
|
||||
CNoteData() : address(), nullifier() { }
|
||||
CNoteData(libzcash::PaymentAddress a) : address {a}, nullifier() { }
|
||||
CNoteData(libzcash::PaymentAddress a, uint256 n) : address {a}, nullifier {n} { }
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
@@ -704,7 +711,59 @@ public:
|
||||
nWitnessCacheSize = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* The reverse mapping of nullifiers to notes.
|
||||
*
|
||||
* The mapping cannot be updated while an encrypted wallet is locked,
|
||||
* because we need the SpendingKey to create the nullifier (#1502). This has
|
||||
* several implications for transactions added to the wallet while locked:
|
||||
*
|
||||
* - Parent transactions can't be marked dirty when a child transaction that
|
||||
* spends their output notes is updated.
|
||||
*
|
||||
* - We currently don't cache any note values, so this is not a problem,
|
||||
* yet.
|
||||
*
|
||||
* - GetFilteredNotes can't filter out spent notes.
|
||||
*
|
||||
* - Per the comment in CNoteData, we assume that if we don't have a
|
||||
* cached nullifier, the note is not spent.
|
||||
*
|
||||
* Another more problematic implication is that the wallet can fail to
|
||||
* detect transactions on the blockchain that spend our notes. There are two
|
||||
* possible cases in which this could happen:
|
||||
*
|
||||
* - We receive a note when the wallet is locked, and then spend it using a
|
||||
* different wallet client.
|
||||
*
|
||||
* - We spend from a PaymentAddress we control, then we export the
|
||||
* SpendingKey and import it into a new wallet, and reindex/rescan to find
|
||||
* the old transactions.
|
||||
*
|
||||
* The wallet will only miss "pure" spends - transactions that are only
|
||||
* linked to us by the fact that they contain notes we spent. If it also
|
||||
* sends notes to us, or interacts with our transparent addresses, we will
|
||||
* detect the transaction and add it to the wallet (again without caching
|
||||
* nullifiers for new notes). As by default JoinSplits send change back to
|
||||
* the origin PaymentAddress, the wallet should rarely miss transactions.
|
||||
*
|
||||
* To work around these issues, whenever the wallet is unlocked, we scan all
|
||||
* cached notes, and cache any missing nullifiers. Since the wallet must be
|
||||
* unlocked in order to spend notes, this means that GetFilteredNotes will
|
||||
* always behave correctly within that context (and any other uses will give
|
||||
* correct responses afterwards), for the transactions that the wallet was
|
||||
* able to detect. Any missing transactions can be rediscovered by:
|
||||
*
|
||||
* - Unlocking the wallet (to fill all nullifier caches).
|
||||
*
|
||||
* - Restarting the node with -reindex (which operates on a locked wallet
|
||||
* but with the now-cached nullifiers).
|
||||
*
|
||||
* Several rounds of this may be required to incrementally fill the
|
||||
* nullifier caches of discovered notes.
|
||||
*/
|
||||
std::map<uint256, JSOutPoint> mapNullifiersToNotes;
|
||||
|
||||
std::map<uint256, CWalletTx> mapWallet;
|
||||
|
||||
int64_t nOrderPosNext;
|
||||
@@ -810,6 +869,7 @@ public:
|
||||
TxItems OrderedTxItems(std::list<CAccountingEntry>& acentries, std::string strAccount = "");
|
||||
|
||||
void MarkDirty();
|
||||
bool UpdateNullifierNoteMap();
|
||||
void UpdateNullifierNoteMap(const CWalletTx& wtx);
|
||||
bool AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletDB* pwalletdb);
|
||||
void SyncTransaction(const CTransaction& tx, const CBlock* pblock);
|
||||
@@ -850,6 +910,12 @@ public:
|
||||
|
||||
std::set<CTxDestination> GetAccountAddresses(std::string strAccount) const;
|
||||
|
||||
boost::optional<uint256> GetNoteNullifier(
|
||||
const JSDescription& jsdesc,
|
||||
const libzcash::PaymentAddress& address,
|
||||
const ZCNoteDecryption& dec,
|
||||
const uint256& hSig,
|
||||
uint8_t n) const;
|
||||
mapNoteData_t FindMyNotes(const CTransaction& tx) const;
|
||||
bool IsFromMe(const uint256& nullifier) const;
|
||||
void GetNoteWitnesses(
|
||||
|
||||
Reference in New Issue
Block a user