Skip to content

Commit 706b6f0

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 3c83cc5 commit 706b6f0

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
@@ -942,7 +942,7 @@ void VerifyBestBlockIsAtPreviousBlock(const CBlockIndex* pindex, CCoinsViewCache
942942
assert(hashPrevBlock == view.GetBestBlock());
943943
}
944944

945-
bool CheckEnforcedPoSBlocksAndBIP30(const CChainParams& chainParameters, const CBlock& block, CValidationState& state, const CBlockIndex* pindex, const CCoinsViewCache& view)
945+
bool CheckEnforcedPoSBlocksAndBIP30(const CChainParams& chainParameters, const TransactionUtxoHasher& utxoHasher, const CBlock& block, CValidationState& state, const CBlockIndex* pindex, const CCoinsViewCache& view)
946946
{
947947
if (pindex->nHeight <= chainParameters.LAST_POW_BLOCK() && block.IsProofOfStake())
948948
return state.DoS(100, error("%s : PoS period not active",__func__),
@@ -954,7 +954,7 @@ bool CheckEnforcedPoSBlocksAndBIP30(const CChainParams& chainParameters, const C
954954

955955
// Enforce BIP30.
956956
for (const auto& tx : block.vtx) {
957-
const CCoins* coins = view.AccessCoins(tx.GetHash());
957+
const CCoins* coins = view.AccessCoins(utxoHasher.GetUtxoHash(tx));
958958
if (coins && !coins->IsPruned())
959959
return state.DoS(100, error("%s : tried to overwrite transaction",__func__),
960960
REJECT_INVALID, "bad-txns-BIP30");
@@ -1074,14 +1074,16 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
10741074
LogWalletBalance();
10751075
static const CChainParams& chainParameters = Params();
10761076

1077+
const BlockUtxoHasher utxoHasher;
1078+
10771079
VerifyBestBlockIsAtPreviousBlock(pindex,view);
10781080
if (block.GetHash() == Params().HashGenesisBlock())
10791081
{
10801082
if(!fJustCheck)
10811083
view.SetBestBlock(pindex->GetBlockHash());
10821084
return true;
10831085
}
1084-
if(!CheckEnforcedPoSBlocksAndBIP30(chainParameters,block,state,pindex,view))
1086+
if(!CheckEnforcedPoSBlocksAndBIP30(chainParameters,utxoHasher,block,state,pindex,view))
10851087
{
10861088
return false;
10871089
}
@@ -1097,7 +1099,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
10971099
const int blocksToSkipChecksFor = checkpointsVerifier.GetTotalBlocksEstimate();
10981100
IndexDatabaseUpdates indexDatabaseUpdates;
10991101
CBlockRewards nExpectedMint = subsidiesContainer.blockSubsidiesProvider().GetBlockSubsidity(pindex->nHeight);
1100-
BlockTransactionChecker blockTxChecker(block,state,pindex,view,blocksToSkipChecksFor);
1102+
BlockTransactionChecker blockTxChecker(block, utxoHasher, state, pindex, view, blocksToSkipChecksFor);
11011103

11021104
if(!blockTxChecker.Check(nExpectedMint,fJustCheck,indexDatabaseUpdates))
11031105
{
@@ -2344,6 +2346,7 @@ bool static LoadBlockIndexDB(string& strError)
23442346
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";
23452347
return false;
23462348
}
2349+
const BlockUtxoHasher utxoHasher;
23472350

23482351
std::vector<CTxUndo> vtxundo;
23492352
vtxundo.reserve(block.vtx.size() - 1);
@@ -2353,7 +2356,7 @@ bool static LoadBlockIndexDB(string& strError)
23532356
CTxUndo undoDummy;
23542357
if (i > 0)
23552358
vtxundo.push_back(CTxUndo());
2356-
UpdateCoinsWithTransaction(block.vtx[i], view, i == 0 ? undoDummy : vtxundo.back(), pindex->nHeight);
2359+
UpdateCoinsWithTransaction(block.vtx[i], view, i == 0 ? undoDummy : vtxundo.back(), utxoHasher, pindex->nHeight);
23572360
view.SetBestBlock(hashBlock);
23582361
}
23592362

0 commit comments

Comments
 (0)