Add sapling nullifiers to db and mempool
This commit is contained in:
@@ -218,7 +218,11 @@ void CCoinsViewCache::SetNullifiers(const CTransaction& tx, bool spent) {
|
|||||||
ret.first->second.flags |= CNullifiersCacheEntry::DIRTY;
|
ret.first->second.flags |= CNullifiersCacheEntry::DIRTY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO add sapling nullifiers
|
for (const SpendDescription &spendDescription : tx.vShieldedSpend) {
|
||||||
|
std::pair<CNullifiersMap::iterator, bool> ret = cacheSaplingNullifiers.insert(std::make_pair(spendDescription.nullifier, CNullifiersCacheEntry()));
|
||||||
|
ret.first->second.entered = spent;
|
||||||
|
ret.first->second.flags |= CNullifiersCacheEntry::DIRTY;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CCoinsViewCache::GetCoins(const uint256 &txid, CCoins &coins) const {
|
bool CCoinsViewCache::GetCoins(const uint256 &txid, CCoins &coins) const {
|
||||||
@@ -448,7 +452,11 @@ bool CCoinsViewCache::HaveJoinSplitRequirements(const CTransaction& tx) const
|
|||||||
|
|
||||||
intermediates.insert(std::make_pair(tree.root(), tree));
|
intermediates.insert(std::make_pair(tree.root(), tree));
|
||||||
}
|
}
|
||||||
// TODO check sapling nullifiers
|
|
||||||
|
for (const SpendDescription &spendDescription : tx.vShieldedSpend) {
|
||||||
|
if (GetNullifier(spendDescription.nullifier, SAPLING_NULLIFIER)) // Prevent double spends
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1274,7 +1274,10 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO check sapling nullifiers
|
for (const SpendDescription &spendDescription : tx.vShieldedSpend) {
|
||||||
|
if (pool.nullifierExists(spendDescription.nullifier, SAPLING_NULLIFIER))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -172,6 +172,31 @@ public:
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class TxWithNullifiers
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CTransaction tx;
|
||||||
|
uint256 sproutNullifier;
|
||||||
|
uint256 saplingNullifier;
|
||||||
|
|
||||||
|
TxWithNullifiers()
|
||||||
|
{
|
||||||
|
CMutableTransaction mutableTx;
|
||||||
|
|
||||||
|
sproutNullifier = GetRandHash();
|
||||||
|
JSDescription jsd;
|
||||||
|
jsd.nullifiers[0] = sproutNullifier;
|
||||||
|
mutableTx.vjoinsplit.emplace_back(jsd);
|
||||||
|
|
||||||
|
saplingNullifier = GetRandHash();
|
||||||
|
SpendDescription sd;
|
||||||
|
sd.nullifier = saplingNullifier;
|
||||||
|
mutableTx.vShieldedSpend.push_back(sd);
|
||||||
|
|
||||||
|
tx = CTransaction(mutableTx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint256 appendRandomCommitment(ZCIncrementalMerkleTree &tree)
|
uint256 appendRandomCommitment(ZCIncrementalMerkleTree &tree)
|
||||||
@@ -186,19 +211,19 @@ uint256 appendRandomCommitment(ZCIncrementalMerkleTree &tree)
|
|||||||
return cm;
|
return cm;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<CTransaction, uint256> createTxWithNullifier()
|
|
||||||
{
|
|
||||||
CMutableTransaction mutableTx;
|
|
||||||
JSDescription jsd;
|
|
||||||
uint256 nullifier = GetRandHash();
|
|
||||||
mutableTx.vjoinsplit.push_back(jsd);
|
|
||||||
jsd.nullifiers[0] = nullifier;
|
|
||||||
CTransaction tx(mutableTx);
|
|
||||||
return std::make_pair(tx, nullifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_FIXTURE_TEST_SUITE(coins_tests, BasicTestingSetup)
|
BOOST_FIXTURE_TEST_SUITE(coins_tests, BasicTestingSetup)
|
||||||
|
|
||||||
|
void checkNullifierCache(const CCoinsViewCacheTest &cache, const TxWithNullifiers &txWithNullifiers, bool shouldBeInCache) {
|
||||||
|
// Make sure the nullifiers have not gotten mixed up
|
||||||
|
BOOST_CHECK(!cache.GetNullifier(txWithNullifiers.sproutNullifier, SAPLING_NULLIFIER));
|
||||||
|
BOOST_CHECK(!cache.GetNullifier(txWithNullifiers.saplingNullifier, SPROUT_NULLIFIER));
|
||||||
|
// Check if the nullifiers either are or are not in the cache
|
||||||
|
bool containsSproutNullifier = cache.GetNullifier(txWithNullifiers.sproutNullifier, SPROUT_NULLIFIER);
|
||||||
|
bool containsSaplingNullifier = cache.GetNullifier(txWithNullifiers.saplingNullifier, SAPLING_NULLIFIER);
|
||||||
|
BOOST_CHECK(containsSproutNullifier == shouldBeInCache);
|
||||||
|
BOOST_CHECK(containsSaplingNullifier == shouldBeInCache);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(nullifier_regression_test)
|
BOOST_AUTO_TEST_CASE(nullifier_regression_test)
|
||||||
{
|
{
|
||||||
// Correct behavior:
|
// Correct behavior:
|
||||||
@@ -206,16 +231,18 @@ BOOST_AUTO_TEST_CASE(nullifier_regression_test)
|
|||||||
CCoinsViewTest base;
|
CCoinsViewTest base;
|
||||||
CCoinsViewCacheTest cache1(&base);
|
CCoinsViewCacheTest cache1(&base);
|
||||||
|
|
||||||
|
TxWithNullifiers txWithNullifiers;
|
||||||
|
|
||||||
// Insert a nullifier into the base.
|
// Insert a nullifier into the base.
|
||||||
auto txWithNullifier = createTxWithNullifier();
|
cache1.SetNullifiers(txWithNullifiers.tx, true);
|
||||||
cache1.SetNullifiers(txWithNullifier.first, true);
|
checkNullifierCache(cache1, txWithNullifiers, true);
|
||||||
cache1.Flush(); // Flush to base.
|
cache1.Flush(); // Flush to base.
|
||||||
|
|
||||||
// Remove the nullifier from cache
|
// Remove the nullifier from cache
|
||||||
cache1.SetNullifiers(txWithNullifier.first, false);
|
cache1.SetNullifiers(txWithNullifiers.tx, false);
|
||||||
|
|
||||||
// The nullifier now should be `false`.
|
// The nullifier now should be `false`.
|
||||||
BOOST_CHECK(!cache1.GetNullifier(txWithNullifier.second, SPROUT_NULLIFIER));
|
checkNullifierCache(cache1, txWithNullifiers, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also correct behavior:
|
// Also correct behavior:
|
||||||
@@ -223,17 +250,19 @@ BOOST_AUTO_TEST_CASE(nullifier_regression_test)
|
|||||||
CCoinsViewTest base;
|
CCoinsViewTest base;
|
||||||
CCoinsViewCacheTest cache1(&base);
|
CCoinsViewCacheTest cache1(&base);
|
||||||
|
|
||||||
|
TxWithNullifiers txWithNullifiers;
|
||||||
|
|
||||||
// Insert a nullifier into the base.
|
// Insert a nullifier into the base.
|
||||||
auto txWithNullifier = createTxWithNullifier();
|
cache1.SetNullifiers(txWithNullifiers.tx, true);
|
||||||
cache1.SetNullifiers(txWithNullifier.first, true);
|
checkNullifierCache(cache1, txWithNullifiers, true);
|
||||||
cache1.Flush(); // Flush to base.
|
cache1.Flush(); // Flush to base.
|
||||||
|
|
||||||
// Remove the nullifier from cache
|
// Remove the nullifier from cache
|
||||||
cache1.SetNullifiers(txWithNullifier.first, false);
|
cache1.SetNullifiers(txWithNullifiers.tx, false);
|
||||||
cache1.Flush(); // Flush to base.
|
cache1.Flush(); // Flush to base.
|
||||||
|
|
||||||
// The nullifier now should be `false`.
|
// The nullifier now should be `false`.
|
||||||
BOOST_CHECK(!cache1.GetNullifier(txWithNullifier.second, SPROUT_NULLIFIER));
|
checkNullifierCache(cache1, txWithNullifiers, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Works because we bring it from the parent cache:
|
// Works because we bring it from the parent cache:
|
||||||
@@ -242,21 +271,22 @@ BOOST_AUTO_TEST_CASE(nullifier_regression_test)
|
|||||||
CCoinsViewCacheTest cache1(&base);
|
CCoinsViewCacheTest cache1(&base);
|
||||||
|
|
||||||
// Insert a nullifier into the base.
|
// Insert a nullifier into the base.
|
||||||
auto txWithNullifier = createTxWithNullifier();
|
TxWithNullifiers txWithNullifiers;
|
||||||
cache1.SetNullifiers(txWithNullifier.first, true);
|
cache1.SetNullifiers(txWithNullifiers.tx, true);
|
||||||
|
checkNullifierCache(cache1, txWithNullifiers, true);
|
||||||
cache1.Flush(); // Empties cache.
|
cache1.Flush(); // Empties cache.
|
||||||
|
|
||||||
// Create cache on top.
|
// Create cache on top.
|
||||||
{
|
{
|
||||||
// Remove the nullifier.
|
// Remove the nullifier.
|
||||||
CCoinsViewCacheTest cache2(&cache1);
|
CCoinsViewCacheTest cache2(&cache1);
|
||||||
BOOST_CHECK(cache2.GetNullifier(txWithNullifier.second, SPROUT_NULLIFIER));
|
checkNullifierCache(cache2, txWithNullifiers, true);
|
||||||
cache1.SetNullifiers(txWithNullifier.first, false);
|
cache1.SetNullifiers(txWithNullifiers.tx, false);
|
||||||
cache2.Flush(); // Empties cache, flushes to cache1.
|
cache2.Flush(); // Empties cache, flushes to cache1.
|
||||||
}
|
}
|
||||||
|
|
||||||
// The nullifier now should be `false`.
|
// The nullifier now should be `false`.
|
||||||
BOOST_CHECK(!cache1.GetNullifier(txWithNullifier.second, SPROUT_NULLIFIER));
|
checkNullifierCache(cache1, txWithNullifiers, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Was broken:
|
// Was broken:
|
||||||
@@ -265,20 +295,20 @@ BOOST_AUTO_TEST_CASE(nullifier_regression_test)
|
|||||||
CCoinsViewCacheTest cache1(&base);
|
CCoinsViewCacheTest cache1(&base);
|
||||||
|
|
||||||
// Insert a nullifier into the base.
|
// Insert a nullifier into the base.
|
||||||
auto txWithNullifier = createTxWithNullifier();
|
TxWithNullifiers txWithNullifiers;
|
||||||
cache1.SetNullifiers(txWithNullifier.first, true);
|
cache1.SetNullifiers(txWithNullifiers.tx, true);
|
||||||
cache1.Flush(); // Empties cache.
|
cache1.Flush(); // Empties cache.
|
||||||
|
|
||||||
// Create cache on top.
|
// Create cache on top.
|
||||||
{
|
{
|
||||||
// Remove the nullifier.
|
// Remove the nullifier.
|
||||||
CCoinsViewCacheTest cache2(&cache1);
|
CCoinsViewCacheTest cache2(&cache1);
|
||||||
cache2.SetNullifiers(txWithNullifier.first, false);
|
cache2.SetNullifiers(txWithNullifiers.tx, false);
|
||||||
cache2.Flush(); // Empties cache, flushes to cache1.
|
cache2.Flush(); // Empties cache, flushes to cache1.
|
||||||
}
|
}
|
||||||
|
|
||||||
// The nullifier now should be `false`.
|
// The nullifier now should be `false`.
|
||||||
BOOST_CHECK(!cache1.GetNullifier(txWithNullifier.second, SPROUT_NULLIFIER));
|
checkNullifierCache(cache1, txWithNullifiers, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -447,26 +477,22 @@ BOOST_AUTO_TEST_CASE(nullifiers_test)
|
|||||||
CCoinsViewTest base;
|
CCoinsViewTest base;
|
||||||
CCoinsViewCacheTest cache(&base);
|
CCoinsViewCacheTest cache(&base);
|
||||||
|
|
||||||
auto txWithNullifier = createTxWithNullifier();
|
TxWithNullifiers txWithNullifiers;
|
||||||
|
checkNullifierCache(cache, txWithNullifiers, false);
|
||||||
BOOST_CHECK(!cache.GetNullifier(txWithNullifier.second, SPROUT_NULLIFIER));
|
cache.SetNullifiers(txWithNullifiers.tx, true);
|
||||||
BOOST_CHECK(!cache.GetNullifier(txWithNullifier.second, SAPLING_NULLIFIER));
|
checkNullifierCache(cache, txWithNullifiers, true);
|
||||||
cache.SetNullifiers(txWithNullifier.first, true);
|
|
||||||
BOOST_CHECK(cache.GetNullifier(txWithNullifier.second, SPROUT_NULLIFIER));
|
|
||||||
BOOST_CHECK(!cache.GetNullifier(txWithNullifier.second, SAPLING_NULLIFIER));
|
|
||||||
cache.Flush();
|
cache.Flush();
|
||||||
|
|
||||||
CCoinsViewCacheTest cache2(&base);
|
CCoinsViewCacheTest cache2(&base);
|
||||||
|
|
||||||
BOOST_CHECK(cache2.GetNullifier(txWithNullifier.second, SPROUT_NULLIFIER));
|
checkNullifierCache(cache2, txWithNullifiers, true);
|
||||||
BOOST_CHECK(!cache2.GetNullifier(txWithNullifier.second, SAPLING_NULLIFIER));
|
cache2.SetNullifiers(txWithNullifiers.tx, false);
|
||||||
cache2.SetNullifiers(txWithNullifier.first, false);
|
checkNullifierCache(cache2, txWithNullifiers, false);
|
||||||
BOOST_CHECK(!cache2.GetNullifier(txWithNullifier.second, SPROUT_NULLIFIER));
|
|
||||||
cache2.Flush();
|
cache2.Flush();
|
||||||
|
|
||||||
CCoinsViewCacheTest cache3(&base);
|
CCoinsViewCacheTest cache3(&base);
|
||||||
|
|
||||||
BOOST_CHECK(!cache3.GetNullifier(txWithNullifier.second, SPROUT_NULLIFIER));
|
checkNullifierCache(cache3, txWithNullifiers, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(anchors_flush_test)
|
BOOST_AUTO_TEST_CASE(anchors_flush_test)
|
||||||
|
|||||||
@@ -110,7 +110,9 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry,
|
|||||||
mapNullifiers[nf] = &tx;
|
mapNullifiers[nf] = &tx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO add sapling nullifiers
|
for (const SpendDescription &spendDescription : tx.vShieldedSpend) {
|
||||||
|
mapSaplingNullifiers[spendDescription.nullifier] = &tx;
|
||||||
|
}
|
||||||
nTransactionsUpdated++;
|
nTransactionsUpdated++;
|
||||||
totalTxSize += entry.GetTxSize();
|
totalTxSize += entry.GetTxSize();
|
||||||
cachedInnerUsage += entry.DynamicMemoryUsage();
|
cachedInnerUsage += entry.DynamicMemoryUsage();
|
||||||
@@ -160,8 +162,9 @@ void CTxMemPool::remove(const CTransaction &origTx, std::list<CTransaction>& rem
|
|||||||
mapNullifiers.erase(nf);
|
mapNullifiers.erase(nf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO remove sapling nullifiers
|
for (const SpendDescription &spendDescription : tx.vShieldedSpend) {
|
||||||
|
mapSaplingNullifiers.erase(spendDescription.nullifier);
|
||||||
|
}
|
||||||
removed.push_back(tx);
|
removed.push_back(tx);
|
||||||
totalTxSize -= mapTx.find(hash)->GetTxSize();
|
totalTxSize -= mapTx.find(hash)->GetTxSize();
|
||||||
cachedInnerUsage -= mapTx.find(hash)->DynamicMemoryUsage();
|
cachedInnerUsage -= mapTx.find(hash)->DynamicMemoryUsage();
|
||||||
@@ -249,13 +252,18 @@ void CTxMemPool::removeConflicts(const CTransaction &tx, std::list<CTransaction>
|
|||||||
if (it != mapNullifiers.end()) {
|
if (it != mapNullifiers.end()) {
|
||||||
const CTransaction &txConflict = *it->second;
|
const CTransaction &txConflict = *it->second;
|
||||||
if (txConflict != tx)
|
if (txConflict != tx)
|
||||||
{
|
|
||||||
remove(txConflict, removed, true);
|
remove(txConflict, removed, true);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO remove sapling nullifiers
|
for (const SpendDescription &spendDescription : tx.vShieldedSpend) {
|
||||||
|
std::map<uint256, const CTransaction*>::iterator it = mapSaplingNullifiers.find(spendDescription.nullifier);
|
||||||
|
if (it != mapSaplingNullifiers.end()) {
|
||||||
|
const CTransaction &txConflict = *it->second;
|
||||||
|
if (txConflict != tx)
|
||||||
|
remove(txConflict, removed, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CTxMemPool::removeExpired(unsigned int nBlockHeight)
|
void CTxMemPool::removeExpired(unsigned int nBlockHeight)
|
||||||
@@ -401,7 +409,9 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
|
|||||||
|
|
||||||
intermediates.insert(std::make_pair(tree.root(), tree));
|
intermediates.insert(std::make_pair(tree.root(), tree));
|
||||||
}
|
}
|
||||||
// TODO check sapling nullifiers
|
for (const SpendDescription &spendDescription : tx.vShieldedSpend) {
|
||||||
|
assert(!pcoins->GetNullifier(spendDescription.nullifier, SAPLING_NULLIFIER));
|
||||||
|
}
|
||||||
if (fDependsWait)
|
if (fDependsWait)
|
||||||
waitingOnDependants.push_back(&(*it));
|
waitingOnDependants.push_back(&(*it));
|
||||||
else {
|
else {
|
||||||
|
|||||||
Reference in New Issue
Block a user