diff --git a/src/chain.h b/src/chain.h index b5a1a3ba6..da1b89a3c 100644 --- a/src/chain.h +++ b/src/chain.h @@ -17,6 +17,7 @@ #include static const int SPROUT_VALUE_VERSION = 1001400; +static const int SAPLING_VALUE_VERSION = 1010100; struct CDiskBlockPos { @@ -166,6 +167,15 @@ public: //! Will be boost::none if nChainTx is zero. boost::optional nChainSproutValue; + //! Change in value held by the Sapling circuit over this block. + //! Not a boost::optional because this was added before Sapling activated, so we can + //! rely on the invariant that every block before this was added had nSaplingValue = 0. + CAmount nSaplingValue; + + //! (memory only) Total value held by the Sapling circuit up to and including this block. + //! Will be boost::none if nChainTx is zero. + boost::optional nChainSaplingValue; + //! block header int nVersion; uint256 hashMerkleRoot; @@ -197,6 +207,8 @@ public: nSequenceId = 0; nSproutValue = boost::none; nChainSproutValue = boost::none; + nSaplingValue = 0; + nChainSaplingValue = boost::none; nVersion = 0; hashMerkleRoot = uint256(); @@ -383,6 +395,12 @@ public: if ((s.GetType() & SER_DISK) && (nVersion >= SPROUT_VALUE_VERSION)) { READWRITE(nSproutValue); } + + // Only read/write nSaplingValue if the client version used to create + // this index was storing them. + if ((s.GetType() & SER_DISK) && (nVersion >= SAPLING_VALUE_VERSION)) { + READWRITE(nSaplingValue); + } } uint256 GetBlockHash() const diff --git a/src/main.cpp b/src/main.cpp index f6c211c3b..ba3f58134 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3162,7 +3162,14 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl pindexNew->nTx = block.vtx.size(); pindexNew->nChainTx = 0; CAmount sproutValue = 0; + CAmount saplingValue = 0; for (auto tx : block.vtx) { + // Negative valueBalance "takes" money from the transparent value pool + // and adds it to the Sapling value pool. Positive valueBalance "gives" + // money to the transparent value pool, removing from the Sapling value + // pool. So we invert the sign here. + saplingValue += -tx.valueBalance; + for (auto js : tx.vjoinsplit) { sproutValue += js.vpub_old; sproutValue -= js.vpub_new; @@ -3170,6 +3177,8 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl } pindexNew->nSproutValue = sproutValue; pindexNew->nChainSproutValue = boost::none; + pindexNew->nSaplingValue = saplingValue; + pindexNew->nChainSaplingValue = boost::none; pindexNew->nFile = pos.nFile; pindexNew->nDataPos = pos.nPos; pindexNew->nUndoPos = 0; @@ -3193,8 +3202,14 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl } else { pindex->nChainSproutValue = boost::none; } + if (pindex->pprev->nChainSaplingValue) { + pindex->nChainSaplingValue = *pindex->pprev->nChainSaplingValue + pindex->nSaplingValue; + } else { + pindex->nChainSaplingValue = boost::none; + } } else { pindex->nChainSproutValue = pindex->nSproutValue; + pindex->nChainSaplingValue = pindex->nSaplingValue; } { LOCK(cs_nBlockSequenceId); @@ -3877,14 +3892,21 @@ bool static LoadBlockIndexDB() } else { pindex->nChainSproutValue = boost::none; } + if (pindex->pprev->nChainSaplingValue) { + pindex->nChainSaplingValue = *pindex->pprev->nChainSaplingValue + pindex->nSaplingValue; + } else { + pindex->nChainSaplingValue = boost::none; + } } else { pindex->nChainTx = 0; pindex->nChainSproutValue = boost::none; + pindex->nChainSaplingValue = boost::none; mapBlocksUnlinked.insert(std::make_pair(pindex->pprev, pindex)); } } else { pindex->nChainTx = pindex->nTx; pindex->nChainSproutValue = pindex->nSproutValue; + pindex->nChainSaplingValue = pindex->nSaplingValue; } } // Construct in-memory chain of branch IDs. diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 89ada7cff..effddc37e 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -159,6 +159,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx UniValue valuePools(UniValue::VARR); valuePools.push_back(ValuePoolDesc("sprout", blockindex->nChainSproutValue, blockindex->nSproutValue)); + valuePools.push_back(ValuePoolDesc("sapling", blockindex->nChainSaplingValue, blockindex->nSaplingValue)); result.push_back(Pair("valuePools", valuePools)); if (blockindex->pprev) @@ -776,6 +777,7 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp) CBlockIndex* tip = chainActive.Tip(); UniValue valuePools(UniValue::VARR); valuePools.push_back(ValuePoolDesc("sprout", tip->nChainSproutValue, boost::none)); + valuePools.push_back(ValuePoolDesc("sapling", tip->nChainSaplingValue, boost::none)); obj.push_back(Pair("valuePools", valuePools)); const Consensus::Params& consensusParams = Params().GetConsensus();