Add caching of incremental witnesses for spendable notes

This commit is contained in:
Jack Grigg
2016-08-24 15:52:27 +12:00
parent 8db7e25c3f
commit be74c80deb
6 changed files with 367 additions and 1 deletions

View File

@@ -1,3 +1,4 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <sodium.h>
@@ -10,8 +11,32 @@
#include "zcash/Note.hpp"
#include "zcash/NoteEncryption.hpp"
using ::testing::_;
using ::testing::Return;
ZCJoinSplit* params = ZCJoinSplit::Unopened();
class MockCCoinsViewCache : public CCoinsViewCache {
public:
MockCCoinsViewCache() : CCoinsViewCache(NULL) { };
MOCK_CONST_METHOD2(GetAnchorAt, bool(const uint256 &rt, ZCIncrementalMerkleTree &tree));
};
class TestWallet : public CWallet {
public:
TestWallet() : CWallet() { }
void IncrementNoteWitnesses(const CBlockIndex* pindex,
const CBlock* pblock,
const CCoinsViewCache* pcoins) {
CWallet::IncrementNoteWitnesses(pindex, pblock, pcoins);
}
void DecrementNoteWitnesses() {
CWallet::DecrementNoteWitnesses();
}
};
CWalletTx GetValidReceive(const libzcash::SpendingKey& sk, CAmount value, bool randomInputs) {
CMutableTransaction mtx;
mtx.nVersion = 2; // Enable JoinSplits
@@ -272,3 +297,132 @@ TEST(wallet_tests, navigate_from_nullifier_to_note) {
EXPECT_EQ(0, wallet.mapNullifiers[nullifier].js);
EXPECT_EQ(1, wallet.mapNullifiers[nullifier].n);
}
TEST(wallet_tests, cached_witnesses_empty_chain) {
TestWallet wallet;
auto sk = libzcash::SpendingKey::random();
wallet.AddSpendingKey(sk);
auto wtx = GetValidReceive(sk, 10, true);
auto note = GetNote(sk, wtx, 0, 1);
auto nullifier = note.nullifier(sk);
mapNoteData_t noteData;
JSOutPoint jsoutpt {wtx.GetTxid(), 0, 1};
CNoteData nd {sk.address(), nullifier};
noteData[jsoutpt] = nd;
wtx.SetNoteData(noteData);
std::vector<JSOutPoint> notes {jsoutpt};
std::vector<boost::optional<ZCIncrementalWitness>> witnesses;
uint256 anchor;
wallet.GetNoteWitnesses(notes, witnesses, anchor);
EXPECT_FALSE((bool) witnesses[0]);
wallet.AddToWallet(wtx, true, NULL);
witnesses.clear();
wallet.GetNoteWitnesses(notes, witnesses, anchor);
EXPECT_FALSE((bool) witnesses[0]);
CBlock block;
block.vtx.push_back(wtx);
MockCCoinsViewCache coins;
// Empty chain, so we shouldn't try to fetch an anchor
EXPECT_CALL(coins, GetAnchorAt(_, _)).Times(0);
wallet.IncrementNoteWitnesses(NULL, &block, &coins);
witnesses.clear();
wallet.GetNoteWitnesses(notes, witnesses, anchor);
EXPECT_TRUE((bool) witnesses[0]);
wallet.DecrementNoteWitnesses();
witnesses.clear();
wallet.GetNoteWitnesses(notes, witnesses, anchor);
EXPECT_FALSE((bool) witnesses[0]);
}
TEST(wallet_tests, cached_witnesses_chain_tip) {
TestWallet wallet;
MockCCoinsViewCache coins;
uint256 anchor1;
CBlock block1;
auto sk = libzcash::SpendingKey::random();
wallet.AddSpendingKey(sk);
{
// First transaction (case tested in _empty_chain)
auto wtx = GetValidReceive(sk, 10, true);
auto note = GetNote(sk, wtx, 0, 1);
auto nullifier = note.nullifier(sk);
mapNoteData_t noteData;
JSOutPoint jsoutpt {wtx.GetTxid(), 0, 1};
CNoteData nd {sk.address(), nullifier};
noteData[jsoutpt] = nd;
wtx.SetNoteData(noteData);
wallet.AddToWallet(wtx, true, NULL);
std::vector<JSOutPoint> notes {jsoutpt};
std::vector<boost::optional<ZCIncrementalWitness>> witnesses;
// First block (case tested in _empty_chain)
block1.vtx.push_back(wtx);
EXPECT_CALL(coins, GetAnchorAt(_, _))
.Times(0);
wallet.IncrementNoteWitnesses(NULL, &block1, &coins);
// Called to fetch anchor
wallet.GetNoteWitnesses(notes, witnesses, anchor1);
}
{
// Second transaction
auto wtx = GetValidReceive(sk, 50, true);
auto note = GetNote(sk, wtx, 0, 1);
auto nullifier = note.nullifier(sk);
mapNoteData_t noteData;
JSOutPoint jsoutpt {wtx.GetTxid(), 0, 1};
CNoteData nd {sk.address(), nullifier};
noteData[jsoutpt] = nd;
wtx.SetNoteData(noteData);
wallet.AddToWallet(wtx, true, NULL);
std::vector<JSOutPoint> notes {jsoutpt};
std::vector<boost::optional<ZCIncrementalWitness>> witnesses;
uint256 anchor2;
wallet.GetNoteWitnesses(notes, witnesses, anchor2);
EXPECT_FALSE((bool) witnesses[0]);
// Second block
CBlock block2;
block2.hashPrevBlock = block1.GetHash();
block2.vtx.push_back(wtx);
EXPECT_CALL(coins, GetAnchorAt(anchor1, _))
.Times(2)
.WillRepeatedly(Return(true));
wallet.IncrementNoteWitnesses(NULL, &block2, &coins);
witnesses.clear();
wallet.GetNoteWitnesses(notes, witnesses, anchor2);
EXPECT_TRUE((bool) witnesses[0]);
EXPECT_NE(anchor1, anchor2);
// Decrementing should give us the previous anchor
uint256 anchor3;
wallet.DecrementNoteWitnesses();
witnesses.clear();
wallet.GetNoteWitnesses(notes, witnesses, anchor3);
EXPECT_FALSE((bool) witnesses[0]);
EXPECT_EQ(anchor1, anchor3);
// Re-incrementing with the same block should give the same result
uint256 anchor4;
wallet.IncrementNoteWitnesses(NULL, &block2, &coins);
witnesses.clear();
wallet.GetNoteWitnesses(notes, witnesses, anchor4);
EXPECT_TRUE((bool) witnesses[0]);
EXPECT_EQ(anchor2, anchor4);
}
}