Skip to content

Commit 789438d

Browse files
committed
Add TransactionUtxoHasher abstraction.
Some places in the code need to determine the hash by which the UTXOs of a given transaction will be referred to; in particular, we need that when processing UTXO set updates for block connects and disconnects, in the mempool and for assembling transactions into a new block. This commit introduces a class TransactionUtxoHasher, which abstracts this step and is used in all those places in the code instead of just getting the txid directly. For now, this has no effects on behaviour; but it makes it more clear in the code where we need this particular logical feature; it will allow us to add some more unit tests for those parts with explicit mocks of the hasher class; and it will make it easier to implement segwit-light in the future (where we basically just need to flip the hasher implementation but no other parts in the code).
1 parent c3dcd66 commit 789438d

12 files changed

+164
-40
lines changed

divi/src/ActiveChainManager.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,19 +87,21 @@ bool ActiveChainManager::DisconnectBlock(
8787
return error("DisconnectBlock() : block and undo data inconsistent");
8888
}
8989

90+
const BlockUtxoHasher utxoHasher;
91+
9092
bool fClean = true;
9193
IndexDatabaseUpdates indexDBUpdates;
9294
// undo transactions in reverse order
9395
for (int transactionIndex = block.vtx.size() - 1; transactionIndex >= 0; transactionIndex--) {
9496
const CTransaction& tx = block.vtx[transactionIndex];
95-
const TxReversalStatus status = UpdateCoinsReversingTransaction(tx,view,blockUndo.vtxundo[transactionIndex-1],pindex->nHeight);
97+
const TransactionLocationReference txLocationReference(utxoHasher, tx, pindex->nHeight, transactionIndex);
98+
const TxReversalStatus status = UpdateCoinsReversingTransaction(tx, txLocationReference, view, blockUndo.vtxundo[transactionIndex-1]);
9699
if(!CheckTxReversalStatus(status,fClean))
97100
{
98101
return false;
99102
}
100103
if(!pfClean)
101104
{
102-
TransactionLocationReference txLocationReference(tx.GetHash(),pindex->nHeight,transactionIndex);
103105
IndexDatabaseUpdateCollector::ReverseTransaction(tx,txLocationReference,view,indexDBUpdates);
104106
}
105107
}

divi/src/BlockMemoryPoolTransactionCollector.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ std::vector<TxPriority> BlockMemoryPoolTransactionCollector::PrioritizeMempoolTr
204204
// Read prev transaction
205205
if (!view.HaveCoins(txin.prevout.hash)) {
206206
CTransaction prevTx;
207-
if(!mempool_.lookup(txin.prevout.hash, prevTx)) {
207+
if(!mempool_.lookupOutpoint(txin.prevout.hash, prevTx)) {
208208
// This should never happen; all transactions in the memory
209209
// pool should connect to either transactions in the chain
210210
// or other transactions in the memory pool.
@@ -335,10 +335,10 @@ std::vector<PrioritizedTransactionData> BlockMemoryPoolTransactionCollector::Pri
335335
nBlockSigOps += nTxSigOps;
336336

337337
CTxUndo txundo;
338-
UpdateCoinsWithTransaction(tx, view, txundo, nHeight);
338+
UpdateCoinsWithTransaction(tx, view, txundo, mempool_.GetUtxoHasher(), nHeight);
339339

340340
// Add transactions that depend on this one to the priority queue
341-
AddDependingTransactionsToPriorityQueue(dependentTransactions, hash, vecPriority, comparer);
341+
AddDependingTransactionsToPriorityQueue(dependentTransactions, mempool_.GetUtxoHasher().GetUtxoHash(tx), vecPriority, comparer);
342342
}
343343

344344
LogPrintf("CreateNewBlock(): total size %u\n", nBlockSize);

divi/src/BlockTransactionChecker.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,14 @@ void TransactionLocationRecorder::RecordTxLocationData(
3838

3939
BlockTransactionChecker::BlockTransactionChecker(
4040
const CBlock& block,
41+
const TransactionUtxoHasher& utxoHasher,
4142
CValidationState& state,
4243
CBlockIndex* pindex,
4344
CCoinsViewCache& view,
4445
const int blocksToSkipChecksFor
4546
): blockundo_(block.vtx.size() - 1)
4647
, block_(block)
48+
, utxoHasher_(utxoHasher)
4749
, state_(state)
4850
, pindex_(pindex)
4951
, view_(view)
@@ -59,7 +61,7 @@ bool BlockTransactionChecker::Check(const CBlockRewards& nExpectedMint,bool fJus
5961
pindex_->nMint = 0;
6062
for (unsigned int i = 0; i < block_.vtx.size(); i++) {
6163
const CTransaction& tx = block_.vtx[i];
62-
const TransactionLocationReference txLocationRef(tx.GetHash(),pindex_->nHeight,i);
64+
const TransactionLocationReference txLocationRef(utxoHasher_, tx, pindex_->nHeight, i);
6365

6466
if(!txInputChecker_.TotalSigOpsAreBelowMaximum(tx))
6567
{
@@ -79,7 +81,7 @@ bool BlockTransactionChecker::Check(const CBlockRewards& nExpectedMint,bool fJus
7981
}
8082

8183
IndexDatabaseUpdateCollector::RecordTransaction(tx,txLocationRef,view_, indexDatabaseUpdates);
82-
UpdateCoinsWithTransaction(tx, view_, blockundo_.vtxundo[i>0u? i-1: 0u], pindex_->nHeight);
84+
UpdateCoinsWithTransaction(tx, view_, blockundo_.vtxundo[i>0u? i-1: 0u], utxoHasher_, pindex_->nHeight);
8385
txLocationRecorder_.RecordTxLocationData(tx,indexDatabaseUpdates.txLocationData);
8486
}
8587
return true;
@@ -93,4 +95,4 @@ bool BlockTransactionChecker::WaitForScriptsToBeChecked()
9395
CBlockUndo& BlockTransactionChecker::getBlockUndoData()
9496
{
9597
return blockundo_;
96-
}
98+
}

divi/src/BlockTransactionChecker.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class CBlock;
1313
class CValidationState;
1414
class CCoinsViewCache;
1515
class CBlockRewards;
16+
class TransactionUtxoHasher;
1617

1718
class TransactionLocationRecorder
1819
{
@@ -35,6 +36,7 @@ class BlockTransactionChecker
3536
private:
3637
CBlockUndo blockundo_;
3738
const CBlock& block_;
39+
const TransactionUtxoHasher& utxoHasher_;
3840
CValidationState& state_;
3941
CBlockIndex* pindex_;
4042
CCoinsViewCache& view_;
@@ -43,6 +45,7 @@ class BlockTransactionChecker
4345
public:
4446
BlockTransactionChecker(
4547
const CBlock& block,
48+
const TransactionUtxoHasher& utxoHasher,
4649
CValidationState& state,
4750
CBlockIndex* pindex,
4851
CCoinsViewCache& view,
@@ -56,4 +59,4 @@ class BlockTransactionChecker
5659
CBlockUndo& getBlockUndoData();
5760
};
5861

59-
#endif// BLOCK_TRANSACTION_CHECKER_H
62+
#endif// BLOCK_TRANSACTION_CHECKER_H

divi/src/IndexDatabaseUpdateCollector.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ void CollectUpdatesFromOutputs(
8181
std::make_pair(CAddressIndexKey(addressType, uint160(hashBytes), txLocationRef.blockHeight, txLocationRef.transactionIndex, txLocationRef.hash, k, false), out.nValue));
8282
// record unspent output
8383
indexDatabaseUpdates.addressUnspentIndex.push_back(
84-
std::make_pair(CAddressUnspentKey(addressType, uint160(hashBytes), txLocationRef.hash, k), CAddressUnspentValue(out.nValue, out.scriptPubKey, txLocationRef.blockHeight)));
84+
std::make_pair(CAddressUnspentKey(addressType, uint160(hashBytes), txLocationRef.utxoHash, k), CAddressUnspentValue(out.nValue, out.scriptPubKey, txLocationRef.blockHeight)));
8585
} else {
8686
continue;
8787
}
@@ -156,7 +156,7 @@ static void CollectUpdatesFromOutputs(
156156
// undo unspent index
157157
indexDBUpdates.addressUnspentIndex.push_back(
158158
std::make_pair(
159-
CAddressUnspentKey(addressType, uint160(hashBytes), txLocationReference.hash, k),
159+
CAddressUnspentKey(addressType, uint160(hashBytes), txLocationReference.utxoHash, k),
160160
CAddressUnspentValue()));
161161

162162
}
@@ -182,4 +182,4 @@ void IndexDatabaseUpdateCollector::ReverseTransaction(
182182
{
183183
ReverseSpending::CollectUpdatesFromOutputs(tx,txLocationRef,indexDatabaseUpdates);
184184
ReverseSpending::CollectUpdatesFromInputs(tx,txLocationRef,view, indexDatabaseUpdates);
185-
}
185+
}

divi/src/IndexDatabaseUpdates.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
#include <IndexDatabaseUpdates.h>
22

3+
#include <primitives/transaction.h>
4+
#include <UtxoCheckingAndUpdating.h>
5+
36
IndexDatabaseUpdates::IndexDatabaseUpdates(
47
): addressIndex()
58
, addressUnspentIndex()
@@ -9,11 +12,13 @@ IndexDatabaseUpdates::IndexDatabaseUpdates(
912
}
1013

1114
TransactionLocationReference::TransactionLocationReference(
12-
uint256 hashValue,
15+
const TransactionUtxoHasher& utxoHasher,
16+
const CTransaction& tx,
1317
unsigned blockheightValue,
1418
int transactionIndexValue
15-
): hash(hashValue)
19+
): hash(tx.GetHash())
20+
, utxoHash(utxoHasher.GetUtxoHash(tx))
1621
, blockHeight(blockheightValue)
1722
, transactionIndex(transactionIndexValue)
1823
{
19-
}
24+
}

divi/src/IndexDatabaseUpdates.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
#include <spentindex.h>
77
#include <uint256.h>
88

9+
class CTransaction;
10+
class TransactionUtxoHasher;
11+
912
/** One entry in the tx index, which locates transactions on disk by their txid
1013
* or bare txid (both keys are possible). */
1114
struct TxIndexEntry
@@ -32,11 +35,13 @@ struct IndexDatabaseUpdates
3235
struct TransactionLocationReference
3336
{
3437
uint256 hash;
38+
uint256 utxoHash;
3539
unsigned blockHeight;
3640
int transactionIndex;
3741

3842
TransactionLocationReference(
39-
uint256 hashValue,
43+
const TransactionUtxoHasher& utxoHasher,
44+
const CTransaction& tx,
4045
unsigned blockheightValue,
4146
int transactionIndexValue);
4247
};

divi/src/UtxoCheckingAndUpdating.cpp

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,35 @@
1010
#include <undo.h>
1111
#include <chainparams.h>
1212
#include <defaultValues.h>
13+
#include <IndexDatabaseUpdates.h>
1314

1415
extern BlockMap mapBlockIndex;
1516

16-
void UpdateCoinsWithTransaction(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo& txundo, int nHeight)
17+
uint256 BlockUtxoHasher::GetUtxoHash(const CTransaction& tx) const
18+
{
19+
return tx.GetHash();
20+
}
21+
22+
void UpdateCoinsWithTransaction(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo& txundo,
23+
const TransactionUtxoHasher& hasher, const int nHeight)
1724
{
1825
// mark inputs spent
1926
if (!tx.IsCoinBase() ) {
2027
txundo.vprevout.reserve(tx.vin.size());
21-
BOOST_FOREACH (const CTxIn& txin, tx.vin) {
28+
for (const auto& txin : tx.vin) {
2229
txundo.vprevout.push_back(CTxInUndo());
2330
bool ret = inputs.ModifyCoins(txin.prevout.hash)->Spend(txin.prevout.n, txundo.vprevout.back());
2431
assert(ret);
2532
}
2633
}
2734

2835
// add outputs
29-
inputs.ModifyCoins(tx.GetHash())->FromTx(tx, nHeight);
36+
inputs.ModifyCoins(hasher.GetUtxoHash(tx))->FromTx(tx, nHeight);
3037
}
3138

3239
static bool RemoveTxOutputsFromCache(
3340
const CTransaction& tx,
34-
const int blockHeight,
41+
const TransactionLocationReference& txLocationReference,
3542
CCoinsViewCache& view)
3643
{
3744
bool outputsAvailable = true;
@@ -40,10 +47,10 @@ static bool RemoveTxOutputsFromCache(
4047
// have outputs available even in the block itself, so we handle that case
4148
// specially with outsEmpty.
4249
CCoins outsEmpty;
43-
CCoinsModifier outs = view.ModifyCoins(tx.GetHash());
50+
CCoinsModifier outs = view.ModifyCoins(txLocationReference.utxoHash);
4451
outs->ClearUnspendable();
4552

46-
CCoins outsBlock(tx, blockHeight);
53+
CCoins outsBlock(tx, txLocationReference.blockHeight);
4754
// The CCoins serialization does not serialize negative numbers.
4855
// No network rules currently depend on the version here, so an inconsistency is harmless
4956
// but it must be corrected before txout nversion ever influences a network rule.
@@ -88,10 +95,10 @@ static void UpdateCoinsForRestoredInputs(
8895
coins->vout[out.n] = undo.txout;
8996
}
9097

91-
TxReversalStatus UpdateCoinsReversingTransaction(const CTransaction& tx, CCoinsViewCache& inputs, const CTxUndo& txundo, int nHeight)
98+
TxReversalStatus UpdateCoinsReversingTransaction(const CTransaction& tx, const TransactionLocationReference& txLocationReference, CCoinsViewCache& inputs, const CTxUndo& txundo)
9299
{
93100
bool fClean = true;
94-
fClean = fClean && RemoveTxOutputsFromCache(tx,nHeight,inputs);
101+
fClean = fClean && RemoveTxOutputsFromCache(tx, txLocationReference, inputs);
95102
if(tx.IsCoinBase()) return fClean? TxReversalStatus::OK : TxReversalStatus::CONTINUE_WITH_ERRORS;
96103
if (txundo.vprevout.size() != tx.vin.size())
97104
{

divi/src/UtxoCheckingAndUpdating.h

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,64 @@
33
#include <vector>
44
#include <scriptCheck.h>
55
#include <amount.h>
6+
#include <uint256.h>
67

78
class CTransaction;
89
class CValidationState;
910
class CCoinsViewCache;
1011
class CTxUndo;
12+
class TransactionLocationReference;
13+
1114
enum class TxReversalStatus
1215
{
1316
ABORT_NO_OTHER_ERRORS,
1417
ABORT_WITH_OTHER_ERRORS,
1518
CONTINUE_WITH_ERRORS,
1619
OK,
1720
};
18-
void UpdateCoinsWithTransaction(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo& txundo, int nHeight);
19-
TxReversalStatus UpdateCoinsReversingTransaction(const CTransaction& tx, CCoinsViewCache& inputs, const CTxUndo& txundo, int nHeight);
21+
22+
/** Implementations of this interface translate transactions into the hashes
23+
* that will be used to refer to the UTXOs their outputs create.
24+
*
25+
* This class abstracts away the segwit-light fork and its activation
26+
* from the places that need to refer to / update UTXOs.
27+
*
28+
* For unit tests, this class can be subclassed and mocked. */
29+
class TransactionUtxoHasher
30+
{
31+
32+
public:
33+
34+
TransactionUtxoHasher() = default;
35+
virtual ~TransactionUtxoHasher() = default;
36+
37+
TransactionUtxoHasher(const TransactionUtxoHasher&) = delete;
38+
void operator=(const TransactionUtxoHasher&) = delete;
39+
40+
virtual uint256 GetUtxoHash(const CTransaction& tx) const = 0;
41+
42+
};
43+
44+
/** A TransactionUtxoHasher for transactions contained in a particular
45+
* block, e.g. for processing that block's connect or disconnect. Initially
46+
* these are just the txid (as also with upstream Bitcoin), but for
47+
* segwit-light, they are changed to the bare txid.
48+
*
49+
* This class abstracts away the segwit-light fork and its activation
50+
* from the places that need to refer to / update UTXOs.
51+
*
52+
* For unit tests, this class can be subclassed and mocked. */
53+
class BlockUtxoHasher : public TransactionUtxoHasher
54+
{
55+
56+
public:
57+
58+
uint256 GetUtxoHash(const CTransaction& tx) const override;
59+
60+
};
61+
62+
void UpdateCoinsWithTransaction(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo& txundo, const TransactionUtxoHasher& hasher, int nHeight);
63+
TxReversalStatus UpdateCoinsReversingTransaction(const CTransaction& tx, const TransactionLocationReference& txLocationReference, CCoinsViewCache& inputs, const CTxUndo& txundo);
2064
bool CheckInputs(
2165
const CTransaction& tx,
2266
CValidationState& state,
@@ -36,4 +80,4 @@ bool CheckInputs(
3680
bool cacheStore,
3781
std::vector<CScriptCheck>* pvChecks = nullptr,
3882
bool connectBlockDoS = false);
39-
#endif// UTXO_CHECKING_AND_UPDATING_H
83+
#endif// UTXO_CHECKING_AND_UPDATING_H

divi/src/main.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1207,7 +1207,7 @@ void VerifyBestBlockIsAtPreviousBlock(const CBlockIndex* pindex, CCoinsViewCache
12071207
assert(hashPrevBlock == view.GetBestBlock());
12081208
}
12091209

1210-
bool CheckEnforcedPoSBlocksAndBIP30(const CChainParams& chainParameters, const CBlock& block, CValidationState& state, const CBlockIndex* pindex, const CCoinsViewCache& view)
1210+
bool CheckEnforcedPoSBlocksAndBIP30(const CChainParams& chainParameters, const TransactionUtxoHasher& utxoHasher, const CBlock& block, CValidationState& state, const CBlockIndex* pindex, const CCoinsViewCache& view)
12111211
{
12121212
if (pindex->nHeight <= chainParameters.LAST_POW_BLOCK() && block.IsProofOfStake())
12131213
return state.DoS(100, error("%s : PoS period not active",__func__),
@@ -1219,7 +1219,7 @@ bool CheckEnforcedPoSBlocksAndBIP30(const CChainParams& chainParameters, const C
12191219

12201220
// Enforce BIP30.
12211221
for (const auto& tx : block.vtx) {
1222-
const CCoins* coins = view.AccessCoins(tx.GetHash());
1222+
const CCoins* coins = view.AccessCoins(utxoHasher.GetUtxoHash(tx));
12231223
if (coins && !coins->IsPruned())
12241224
return state.DoS(100, error("%s : tried to overwrite transaction",__func__),
12251225
REJECT_INVALID, "bad-txns-BIP30");
@@ -1339,14 +1339,16 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
13391339
LogWalletBalance();
13401340
static const CChainParams& chainParameters = Params();
13411341

1342+
const BlockUtxoHasher utxoHasher;
1343+
13421344
VerifyBestBlockIsAtPreviousBlock(pindex,view);
13431345
if (block.GetHash() == Params().HashGenesisBlock())
13441346
{
13451347
if(!fJustCheck)
13461348
view.SetBestBlock(pindex->GetBlockHash());
13471349
return true;
13481350
}
1349-
if(!CheckEnforcedPoSBlocksAndBIP30(chainParameters,block,state,pindex,view))
1351+
if(!CheckEnforcedPoSBlocksAndBIP30(chainParameters,utxoHasher,block,state,pindex,view))
13501352
{
13511353
return false;
13521354
}
@@ -1363,7 +1365,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
13631365
const int blocksToSkipChecksFor = checkpointsVerifier.GetTotalBlocksEstimate();
13641366
IndexDatabaseUpdates indexDatabaseUpdates;
13651367
CBlockRewards nExpectedMint = subsidiesContainer.blockSubsidiesProvider().GetBlockSubsidity(pindex->nHeight);
1366-
BlockTransactionChecker blockTxChecker(block,state,pindex,view,blocksToSkipChecksFor);
1368+
BlockTransactionChecker blockTxChecker(block, utxoHasher, state, pindex, view, blocksToSkipChecksFor);
13671369

13681370
if(!blockTxChecker.Check(nExpectedMint,fJustCheck,indexDatabaseUpdates))
13691371
{
@@ -2636,6 +2638,7 @@ bool static LoadBlockIndexDB(string& strError)
26362638
strError = "The wallet has been not been closed gracefully and has caused corruption of blocks stored to disk. Data directory is in an unusable state";
26372639
return false;
26382640
}
2641+
const BlockUtxoHasher utxoHasher;
26392642

26402643
std::vector<CTxUndo> vtxundo;
26412644
vtxundo.reserve(block.vtx.size() - 1);
@@ -2645,7 +2648,7 @@ bool static LoadBlockIndexDB(string& strError)
26452648
CTxUndo undoDummy;
26462649
if (i > 0)
26472650
vtxundo.push_back(CTxUndo());
2648-
UpdateCoinsWithTransaction(block.vtx[i], view, i == 0 ? undoDummy : vtxundo.back(), pindex->nHeight);
2651+
UpdateCoinsWithTransaction(block.vtx[i], view, i == 0 ? undoDummy : vtxundo.back(), utxoHasher, pindex->nHeight);
26492652
view.SetBestBlock(hashBlock);
26502653
}
26512654

0 commit comments

Comments
 (0)