Browse Source

Track net value entering and exiting the Sprout circuit

Delta values will be stored for new blocks; old blocks can be filled in by
re-indexing. The net value currently in the Sprout circuit is only calculated
when delta values for all previous blocks are present.
pull/4/head
Jack Grigg 7 years ago
parent
commit
ad6a36ad02
No known key found for this signature in database GPG Key ID: 665DBCD284F7DAFF
  1. 21
      src/chain.h
  2. 86
      src/gtest/test_validation.cpp
  3. 25
      src/main.cpp
  4. 1
      src/txdb.cpp

21
src/chain.h

@ -16,6 +16,8 @@
#include <boost/foreach.hpp>
static const int SPROUT_VALUE_VERSION = 1001400;
struct CDiskBlockPos
{
int nFile;
@ -144,6 +146,15 @@ public:
//! (memory only) The anchor for the tree state up to the end of this block
uint256 hashAnchorEnd;
//! Change in value held by the Sprout circuit over this block.
//! Will be boost::none for older blocks on old nodes until a reindex has taken place.
boost::optional<CAmount> nSproutValue;
//! (memory only) Total value held by the Sprout circuit up to and including this block.
//! Will be boost::none for on old nodes until a reindex has taken place.
//! Will be boost::none if nChainTx is zero.
boost::optional<CAmount> nChainSproutValue;
//! block header
int nVersion;
uint256 hashMerkleRoot;
@ -172,6 +183,8 @@ public:
hashAnchor = uint256();
hashAnchorEnd = uint256();
nSequenceId = 0;
nSproutValue = boost::none;
nChainSproutValue = boost::none;
nVersion = 0;
hashMerkleRoot = uint256();
@ -339,6 +352,14 @@ public:
READWRITE(nBits);
READWRITE(nNonce);
READWRITE(nSolution);
// Only read/write nSproutValue if the client version used to create
// this index was storing them.
// TODO: See if we can get away with not serializing the boost::optional
// one-byte header, without requiring users to reindex on upgrade.
if (nType & SER_DISK && nVersion >= SPROUT_VALUE_VERSION) {
READWRITE(nSproutValue);
}
}
uint256 GetBlockHash() const

86
src/gtest/test_validation.cpp

@ -2,6 +2,18 @@
#include "consensus/validation.h"
#include "main.h"
#include "utiltest.h"
extern ZCJoinSplit* params;
extern bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBlockIndex *pindexNew, const CDiskBlockPos& pos);
void ExpectOptionalAmount(CAmount expected, boost::optional<CAmount> actual) {
EXPECT_TRUE((bool)actual);
if (actual) {
EXPECT_EQ(expected, *actual);
}
}
// Fake an empty view
class FakeCoinsViewDB : public CCoinsView {
@ -61,3 +73,77 @@ TEST(Validation, ContextualCheckInputsPassesWithCoinbase) {
CValidationState state;
EXPECT_TRUE(ContextualCheckInputs(tx, state, view, false, 0, false, Params(CBaseChainParams::MAIN).GetConsensus()));
}
TEST(Validation, ReceivedBlockTransactions) {
auto sk = libzcash::SpendingKey::random();
// Create a fake genesis block
CBlock block1;
block1.vtx.push_back(GetValidReceive(*params, sk, 5, true));
block1.hashMerkleRoot = block1.BuildMerkleTree();
CBlockIndex fakeIndex1 {block1};
// Create a fake child block
CBlock block2;
block2.hashPrevBlock = block1.GetHash();
block2.vtx.push_back(GetValidReceive(*params, sk, 10, true));
block2.hashMerkleRoot = block2.BuildMerkleTree();
CBlockIndex fakeIndex2 {block2};
fakeIndex2.pprev = &fakeIndex1;
CDiskBlockPos pos1;
CDiskBlockPos pos2;
// Set initial state of indices
ASSERT_TRUE(fakeIndex1.RaiseValidity(BLOCK_VALID_TREE));
ASSERT_TRUE(fakeIndex2.RaiseValidity(BLOCK_VALID_TREE));
EXPECT_TRUE(fakeIndex1.IsValid(BLOCK_VALID_TREE));
EXPECT_TRUE(fakeIndex2.IsValid(BLOCK_VALID_TREE));
EXPECT_FALSE(fakeIndex1.IsValid(BLOCK_VALID_TRANSACTIONS));
EXPECT_FALSE(fakeIndex2.IsValid(BLOCK_VALID_TRANSACTIONS));
// Sprout pool values should not be set
EXPECT_FALSE((bool)fakeIndex1.nSproutValue);
EXPECT_FALSE((bool)fakeIndex1.nChainSproutValue);
EXPECT_FALSE((bool)fakeIndex2.nSproutValue);
EXPECT_FALSE((bool)fakeIndex2.nChainSproutValue);
// Mark the second block's transactions as received first
CValidationState state;
EXPECT_TRUE(ReceivedBlockTransactions(block2, state, &fakeIndex2, pos2));
EXPECT_FALSE(fakeIndex1.IsValid(BLOCK_VALID_TRANSACTIONS));
EXPECT_TRUE(fakeIndex2.IsValid(BLOCK_VALID_TRANSACTIONS));
// Sprout pool value delta should now be set for the second block,
// but not any chain totals
EXPECT_FALSE((bool)fakeIndex1.nSproutValue);
EXPECT_FALSE((bool)fakeIndex1.nChainSproutValue);
{
SCOPED_TRACE("ExpectOptionalAmount call");
ExpectOptionalAmount(20, fakeIndex2.nSproutValue);
}
EXPECT_FALSE((bool)fakeIndex2.nChainSproutValue);
// Now mark the first block's transactions as received
EXPECT_TRUE(ReceivedBlockTransactions(block1, state, &fakeIndex1, pos1));
EXPECT_TRUE(fakeIndex1.IsValid(BLOCK_VALID_TRANSACTIONS));
EXPECT_TRUE(fakeIndex2.IsValid(BLOCK_VALID_TRANSACTIONS));
// Sprout pool values should now be set for both blocks
{
SCOPED_TRACE("ExpectOptionalAmount call");
ExpectOptionalAmount(10, fakeIndex1.nSproutValue);
}
{
SCOPED_TRACE("ExpectOptionalAmount call");
ExpectOptionalAmount(10, fakeIndex1.nChainSproutValue);
}
{
SCOPED_TRACE("ExpectOptionalAmount call");
ExpectOptionalAmount(20, fakeIndex2.nSproutValue);
}
{
SCOPED_TRACE("ExpectOptionalAmount call");
ExpectOptionalAmount(30, fakeIndex2.nChainSproutValue);
}
}

25
src/main.cpp

@ -2835,6 +2835,15 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl
{
pindexNew->nTx = block.vtx.size();
pindexNew->nChainTx = 0;
CAmount sproutValue = 0;
for (auto tx : block.vtx) {
for (auto js : tx.vjoinsplit) {
sproutValue += js.vpub_old;
sproutValue -= js.vpub_new;
}
}
pindexNew->nSproutValue = sproutValue;
pindexNew->nChainSproutValue = boost::none;
pindexNew->nFile = pos.nFile;
pindexNew->nDataPos = pos.nPos;
pindexNew->nUndoPos = 0;
@ -2852,6 +2861,15 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl
CBlockIndex *pindex = queue.front();
queue.pop_front();
pindex->nChainTx = (pindex->pprev ? pindex->pprev->nChainTx : 0) + pindex->nTx;
if (pindex->pprev) {
if (pindex->pprev->nChainSproutValue && pindex->nSproutValue) {
pindex->nChainSproutValue = *pindex->pprev->nChainSproutValue + *pindex->nSproutValue;
} else {
pindex->nChainSproutValue = boost::none;
}
} else {
pindex->nChainSproutValue = pindex->nSproutValue;
}
{
LOCK(cs_nBlockSequenceId);
pindex->nSequenceId = nBlockSequenceId++;
@ -3522,12 +3540,19 @@ bool static LoadBlockIndexDB()
if (pindex->pprev) {
if (pindex->pprev->nChainTx) {
pindex->nChainTx = pindex->pprev->nChainTx + pindex->nTx;
if (pindex->pprev->nChainSproutValue && pindex->nSproutValue) {
pindex->nChainSproutValue = *pindex->pprev->nChainSproutValue + *pindex->nSproutValue;
} else {
pindex->nChainSproutValue = boost::none;
}
} else {
pindex->nChainTx = 0;
pindex->nChainSproutValue = boost::none;
mapBlocksUnlinked.insert(std::make_pair(pindex->pprev, pindex));
}
} else {
pindex->nChainTx = pindex->nTx;
pindex->nChainSproutValue = pindex->nSproutValue;
}
}
if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && (pindex->nChainTx || pindex->pprev == NULL))

1
src/txdb.cpp

@ -310,6 +310,7 @@ bool CBlockTreeDB::LoadBlockIndexGuts()
pindexNew->nSolution = diskindex.nSolution;
pindexNew->nStatus = diskindex.nStatus;
pindexNew->nTx = diskindex.nTx;
pindexNew->nSproutValue = diskindex.nSproutValue;
if (!CheckProofOfWork(pindexNew->GetBlockHash(), pindexNew->nBits, Params().GetConsensus()))
return error("LoadBlockIndex(): CheckProofOfWork failed: %s", pindexNew->ToString());

Loading…
Cancel
Save