From bd80a955e5c0b053e3de2467524fcb0d2ed6df26 Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Tue, 19 Jan 2021 13:17:29 +0100 Subject: [PATCH 01/14] 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). --- divi/src/ActiveChainManager.cpp | 4 +- .../BlockMemoryPoolTransactionCollector.cpp | 4 +- divi/src/BlockTransactionChecker.cpp | 6 ++- divi/src/BlockTransactionChecker.h | 3 ++ divi/src/IndexDatabaseUpdateCollector.cpp | 6 +-- divi/src/IndexDatabaseUpdates.cpp | 3 ++ divi/src/IndexDatabaseUpdates.h | 3 ++ divi/src/UtxoCheckingAndUpdating.cpp | 14 ++++-- divi/src/UtxoCheckingAndUpdating.h | 44 ++++++++++++++++- divi/src/main.cpp | 19 ++++---- divi/src/txmempool.cpp | 48 +++++++++++++++---- divi/src/txmempool.h | 6 +++ 12 files changed, 131 insertions(+), 29 deletions(-) diff --git a/divi/src/ActiveChainManager.cpp b/divi/src/ActiveChainManager.cpp index ba41b5ed1..74bf7e0e8 100644 --- a/divi/src/ActiveChainManager.cpp +++ b/divi/src/ActiveChainManager.cpp @@ -87,12 +87,14 @@ bool ActiveChainManager::DisconnectBlock( return error("DisconnectBlock() : block and undo data inconsistent"); } + const BlockUtxoHasher utxoHasher; + bool fClean = true; IndexDatabaseUpdates indexDBUpdates; // undo transactions in reverse order for (int transactionIndex = block.vtx.size() - 1; transactionIndex >= 0; transactionIndex--) { const CTransaction& tx = block.vtx[transactionIndex]; - const TransactionLocationReference txLocationReference(tx, pindex->nHeight, transactionIndex); + const TransactionLocationReference txLocationReference(utxoHasher, tx, pindex->nHeight, transactionIndex); const auto* undo = (transactionIndex > 0 ? &blockUndo.vtxundo[transactionIndex - 1] : nullptr); const TxReversalStatus status = UpdateCoinsReversingTransaction(tx, txLocationReference, view, undo); if(!CheckTxReversalStatus(status,fClean)) diff --git a/divi/src/BlockMemoryPoolTransactionCollector.cpp b/divi/src/BlockMemoryPoolTransactionCollector.cpp index 7bbccf807..dd70a41b8 100644 --- a/divi/src/BlockMemoryPoolTransactionCollector.cpp +++ b/divi/src/BlockMemoryPoolTransactionCollector.cpp @@ -324,10 +324,10 @@ std::vector BlockMemoryPoolTransactionCollector::Pri currentBlockSigOps += transactionSigOpCount; CTxUndo txundo; - UpdateCoinsWithTransaction(tx, view, txundo, nHeight); + UpdateCoinsWithTransaction(tx, view, txundo, mempool_.GetUtxoHasher(), nHeight); // Add transactions that depend on this one to the priority queue - AddDependingTransactionsToPriorityQueue(dependentTransactions, hash, vecPriority, comparer); + AddDependingTransactionsToPriorityQueue(dependentTransactions, mempool_.GetUtxoHasher().GetUtxoHash(tx), vecPriority, comparer); } LogPrintf("%s: total size %u\n",__func__, currentBlockSize); diff --git a/divi/src/BlockTransactionChecker.cpp b/divi/src/BlockTransactionChecker.cpp index 773fa5590..1f867667e 100644 --- a/divi/src/BlockTransactionChecker.cpp +++ b/divi/src/BlockTransactionChecker.cpp @@ -40,6 +40,7 @@ void TransactionLocationRecorder::RecordTxLocationData( BlockTransactionChecker::BlockTransactionChecker( const CBlock& block, + const TransactionUtxoHasher& utxoHasher, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, @@ -48,6 +49,7 @@ BlockTransactionChecker::BlockTransactionChecker( ): blockundo_(block.vtx.size() - 1) , block_(block) , activation_(pindex) + , utxoHasher_(utxoHasher) , state_(state) , pindex_(pindex) , view_(view) @@ -122,7 +124,7 @@ bool BlockTransactionChecker::Check(const CBlockRewards& nExpectedMint,bool fJus for (unsigned int i = 0; i < block_.vtx.size(); i++) { const CTransaction& tx = block_.vtx[i]; - const TransactionLocationReference txLocationRef(tx, pindex_->nHeight, i); + const TransactionLocationReference txLocationRef(utxoHasher_, tx, pindex_->nHeight, i); if(!txInputChecker_.InputsAreValid(tx)) { @@ -147,7 +149,7 @@ bool BlockTransactionChecker::Check(const CBlockRewards& nExpectedMint,bool fJus } IndexDatabaseUpdateCollector::RecordTransaction(tx,txLocationRef,view_, indexDatabaseUpdates); - UpdateCoinsWithTransaction(tx, view_, blockundo_.vtxundo[i>0u? i-1: 0u], pindex_->nHeight); + UpdateCoinsWithTransaction(tx, view_, blockundo_.vtxundo[i>0u? i-1: 0u], utxoHasher_, pindex_->nHeight); txLocationRecorder_.RecordTxLocationData(tx,indexDatabaseUpdates.txLocationData); } return true; diff --git a/divi/src/BlockTransactionChecker.h b/divi/src/BlockTransactionChecker.h index f96ad3fb7..991b71a17 100644 --- a/divi/src/BlockTransactionChecker.h +++ b/divi/src/BlockTransactionChecker.h @@ -15,6 +15,7 @@ class CBlock; class CValidationState; class CCoinsViewCache; class CBlockRewards; +class TransactionUtxoHasher; class TransactionLocationRecorder { @@ -38,6 +39,7 @@ class BlockTransactionChecker CBlockUndo blockundo_; const CBlock& block_; const ActivationState activation_; + const TransactionUtxoHasher& utxoHasher_; CValidationState& state_; CBlockIndex* pindex_; CCoinsViewCache& view_; @@ -58,6 +60,7 @@ class BlockTransactionChecker BlockTransactionChecker( const CBlock& block, + const TransactionUtxoHasher& utxoHasher, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, diff --git a/divi/src/IndexDatabaseUpdateCollector.cpp b/divi/src/IndexDatabaseUpdateCollector.cpp index b6f61e55f..d5057bc43 100644 --- a/divi/src/IndexDatabaseUpdateCollector.cpp +++ b/divi/src/IndexDatabaseUpdateCollector.cpp @@ -88,7 +88,7 @@ void CollectUpdatesFromOutputs( std::make_pair(CAddressIndexKey(addressType, uint160(hashBytes), txLocationRef.blockHeight, txLocationRef.transactionIndex, txLocationRef.hash, k, false), out.nValue)); // record unspent output indexDatabaseUpdates.addressUnspentIndex.push_back( - std::make_pair(CAddressUnspentKey(addressType, uint160(hashBytes), txLocationRef.hash, k), CAddressUnspentValue(out.nValue, out.scriptPubKey, txLocationRef.blockHeight))); + std::make_pair(CAddressUnspentKey(addressType, uint160(hashBytes), txLocationRef.utxoHash, k), CAddressUnspentValue(out.nValue, out.scriptPubKey, txLocationRef.blockHeight))); } else { continue; } @@ -163,7 +163,7 @@ static void CollectUpdatesFromOutputs( // undo unspent index indexDBUpdates.addressUnspentIndex.push_back( std::make_pair( - CAddressUnspentKey(addressType, uint160(hashBytes), txLocationReference.hash, k), + CAddressUnspentKey(addressType, uint160(hashBytes), txLocationReference.utxoHash, k), CAddressUnspentValue())); } @@ -189,4 +189,4 @@ void IndexDatabaseUpdateCollector::ReverseTransaction( { ReverseSpending::CollectUpdatesFromOutputs(tx,txLocationRef,indexDatabaseUpdates); ReverseSpending::CollectUpdatesFromInputs(tx,txLocationRef,view, indexDatabaseUpdates); -} \ No newline at end of file +} diff --git a/divi/src/IndexDatabaseUpdates.cpp b/divi/src/IndexDatabaseUpdates.cpp index 032a69c3c..7ed210ee0 100644 --- a/divi/src/IndexDatabaseUpdates.cpp +++ b/divi/src/IndexDatabaseUpdates.cpp @@ -1,6 +1,7 @@ #include #include +#include IndexDatabaseUpdates::IndexDatabaseUpdates( ): addressIndex() @@ -11,10 +12,12 @@ IndexDatabaseUpdates::IndexDatabaseUpdates( } TransactionLocationReference::TransactionLocationReference( + const TransactionUtxoHasher& utxoHasher, const CTransaction& tx, unsigned blockheightValue, int transactionIndexValue ): hash(tx.GetHash()) + , utxoHash(utxoHasher.GetUtxoHash(tx)) , blockHeight(blockheightValue) , transactionIndex(transactionIndexValue) { diff --git a/divi/src/IndexDatabaseUpdates.h b/divi/src/IndexDatabaseUpdates.h index 0e77d08cc..9abc9857e 100644 --- a/divi/src/IndexDatabaseUpdates.h +++ b/divi/src/IndexDatabaseUpdates.h @@ -7,6 +7,7 @@ #include class CTransaction; +class TransactionUtxoHasher; /** One entry in the tx index, which locates transactions on disk by their txid * or bare txid (both keys are possible). */ @@ -34,10 +35,12 @@ struct IndexDatabaseUpdates struct TransactionLocationReference { uint256 hash; + uint256 utxoHash; unsigned blockHeight; int transactionIndex; TransactionLocationReference( + const TransactionUtxoHasher& utxoHasher, const CTransaction& tx, unsigned blockheightValue, int transactionIndexValue); diff --git a/divi/src/UtxoCheckingAndUpdating.cpp b/divi/src/UtxoCheckingAndUpdating.cpp index f2f094b6e..5d7abb1da 100644 --- a/divi/src/UtxoCheckingAndUpdating.cpp +++ b/divi/src/UtxoCheckingAndUpdating.cpp @@ -12,12 +12,18 @@ #include #include -void UpdateCoinsWithTransaction(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo& txundo, int nHeight) +uint256 BlockUtxoHasher::GetUtxoHash(const CTransaction& tx) const +{ + return tx.GetHash(); +} + +void UpdateCoinsWithTransaction(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo& txundo, + const TransactionUtxoHasher& hasher, const int nHeight) { // mark inputs spent if (!tx.IsCoinBase() ) { txundo.vprevout.reserve(tx.vin.size()); - BOOST_FOREACH (const CTxIn& txin, tx.vin) { + for (const auto& txin : tx.vin) { txundo.vprevout.push_back(CTxInUndo()); bool ret = inputs.ModifyCoins(txin.prevout.hash)->Spend(txin.prevout.n, txundo.vprevout.back()); assert(ret); @@ -25,7 +31,7 @@ void UpdateCoinsWithTransaction(const CTransaction& tx, CCoinsViewCache& inputs, } // add outputs - inputs.ModifyCoins(tx.GetHash())->FromTx(tx, nHeight); + inputs.ModifyCoins(hasher.GetUtxoHash(tx))->FromTx(tx, nHeight); } static bool RemoveTxOutputsFromCache( @@ -39,7 +45,7 @@ static bool RemoveTxOutputsFromCache( // have outputs available even in the block itself, so we handle that case // specially with outsEmpty. CCoins outsEmpty; - CCoinsModifier outs = view.ModifyCoins(txLocationReference.hash); + CCoinsModifier outs = view.ModifyCoins(txLocationReference.utxoHash); outs->ClearUnspendable(); CCoins outsBlock(tx, txLocationReference.blockHeight); diff --git a/divi/src/UtxoCheckingAndUpdating.h b/divi/src/UtxoCheckingAndUpdating.h index 82ad3ac58..da4d76ed5 100644 --- a/divi/src/UtxoCheckingAndUpdating.h +++ b/divi/src/UtxoCheckingAndUpdating.h @@ -3,6 +3,7 @@ #include #include #include +#include class BlockMap; class CTransaction; @@ -18,7 +19,48 @@ enum class TxReversalStatus CONTINUE_WITH_ERRORS, OK, }; -void UpdateCoinsWithTransaction(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo& txundo, int nHeight); + +/** Implementations of this interface translate transactions into the hashes + * that will be used to refer to the UTXOs their outputs create. + * + * This class abstracts away the segwit-light fork and its activation + * from the places that need to refer to / update UTXOs. + * + * For unit tests, this class can be subclassed and mocked. */ +class TransactionUtxoHasher +{ + +public: + + TransactionUtxoHasher() = default; + virtual ~TransactionUtxoHasher() = default; + + TransactionUtxoHasher(const TransactionUtxoHasher&) = delete; + void operator=(const TransactionUtxoHasher&) = delete; + + virtual uint256 GetUtxoHash(const CTransaction& tx) const = 0; + +}; + +/** A TransactionUtxoHasher for transactions contained in a particular + * block, e.g. for processing that block's connect or disconnect. Initially + * these are just the txid (as also with upstream Bitcoin), but for + * segwit-light, they are changed to the bare txid. + * + * This class abstracts away the segwit-light fork and its activation + * from the places that need to refer to / update UTXOs. + * + * For unit tests, this class can be subclassed and mocked. */ +class BlockUtxoHasher : public TransactionUtxoHasher +{ + +public: + + uint256 GetUtxoHash(const CTransaction& tx) const override; + +}; + +void UpdateCoinsWithTransaction(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo& txundo, const TransactionUtxoHasher& hasher, int nHeight); TxReversalStatus UpdateCoinsReversingTransaction(const CTransaction& tx, const TransactionLocationReference& txLocationReference, CCoinsViewCache& inputs, const CTxUndo* txundo); bool CheckInputs( const CTransaction& tx, diff --git a/divi/src/main.cpp b/divi/src/main.cpp index e2b567b3c..25b77d97b 100755 --- a/divi/src/main.cpp +++ b/divi/src/main.cpp @@ -609,7 +609,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState& state, const CTransa view.SetBackend(viewMemPool); // do we already have it? - if (view.HaveCoins(hash)) + if (view.HaveCoins(pool.GetUtxoHasher().GetUtxoHash(tx))) { LogPrint("mempool","%s - tx %s outputs already exist\n",__func__,hash); @@ -907,7 +907,7 @@ void VerifyBestBlockIsAtPreviousBlock(const CBlockIndex* pindex, CCoinsViewCache assert(hashPrevBlock == view.GetBestBlock()); } -bool CheckEnforcedPoSBlocksAndBIP30(const CChainParams& chainParameters, const CBlock& block, CValidationState& state, const CBlockIndex* pindex, const CCoinsViewCache& view) +bool CheckEnforcedPoSBlocksAndBIP30(const CChainParams& chainParameters, const TransactionUtxoHasher& utxoHasher, const CBlock& block, CValidationState& state, const CBlockIndex* pindex, const CCoinsViewCache& view) { if (pindex->nHeight <= chainParameters.LAST_POW_BLOCK() && block.IsProofOfStake()) return state.DoS(100, error("%s : PoS period not active",__func__), @@ -919,7 +919,7 @@ bool CheckEnforcedPoSBlocksAndBIP30(const CChainParams& chainParameters, const C // Enforce BIP30. for (const auto& tx : block.vtx) { - const CCoins* coins = view.AccessCoins(tx.GetHash()); + const CCoins* coins = view.AccessCoins(utxoHasher.GetUtxoHash(tx)); if (coins && !coins->IsPruned()) return state.DoS(100, error("%s : tried to overwrite transaction",__func__), REJECT_INVALID, "bad-txns-BIP30"); @@ -1011,6 +1011,8 @@ bool ConnectBlock( LogWalletBalance(); static const CChainParams& chainParameters = Params(); + const BlockUtxoHasher utxoHasher; + VerifyBestBlockIsAtPreviousBlock(pindex,view); if (block.GetHash() == Params().HashGenesisBlock()) { @@ -1018,7 +1020,7 @@ bool ConnectBlock( view.SetBestBlock(pindex->GetBlockHash()); return true; } - if(!CheckEnforcedPoSBlocksAndBIP30(chainParameters,block,state,pindex,view)) + if(!CheckEnforcedPoSBlocksAndBIP30(chainParameters,utxoHasher,block,state,pindex,view)) { return false; } @@ -1039,7 +1041,7 @@ bool ConnectBlock( nExpectedMint.nStakeReward += nExpectedMint.nMasternodeReward; nExpectedMint.nMasternodeReward = 0; } - BlockTransactionChecker blockTxChecker(block,state,pindex,view,mapBlockIndex,blocksToSkipChecksFor); + BlockTransactionChecker blockTxChecker(block, utxoHasher, state, pindex, view, mapBlockIndex, blocksToSkipChecksFor); if(!blockTxChecker.Check(nExpectedMint,fJustCheck,indexDatabaseUpdates)) { @@ -2238,6 +2240,7 @@ bool static LoadBlockIndexDB(string& strError) 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"; return false; } + const BlockUtxoHasher utxoHasher; std::vector vtxundo; vtxundo.reserve(block.vtx.size() - 1); @@ -2247,7 +2250,7 @@ bool static LoadBlockIndexDB(string& strError) CTxUndo undoDummy; if (i > 0) vtxundo.push_back(CTxUndo()); - UpdateCoinsWithTransaction(block.vtx[i], view, i == 0 ? undoDummy : vtxundo.back(), pindex->nHeight); + UpdateCoinsWithTransaction(block.vtx[i], view, i == 0 ? undoDummy : vtxundo.back(), utxoHasher, pindex->nHeight); view.SetBestBlock(hashBlock); } @@ -3219,7 +3222,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, { mempool.check(pcoinsTip, mapBlockIndex); RelayTransaction(tx); - vWorkQueue.push_back(inv.GetHash()); + vWorkQueue.push_back(mempool.GetUtxoHasher().GetUtxoHash(tx)); LogPrint("mempool", "%s: peer=%d %s : accepted %s (poolsz %u)\n", __func__, @@ -3248,7 +3251,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if(AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2)) { LogPrint("mempool", " accepted orphan tx %s\n", orphanHash); RelayTransaction(orphanTx); - vWorkQueue.push_back(orphanHash); + vWorkQueue.push_back(mempool.GetUtxoHasher().GetUtxoHash(orphanTx)); vEraseQueue.push_back(orphanHash); } else if(!fMissingInputs2) { int nDos = 0; diff --git a/divi/src/txmempool.cpp b/divi/src/txmempool.cpp index bbe3811a1..98fcfe16f 100644 --- a/divi/src/txmempool.cpp +++ b/divi/src/txmempool.cpp @@ -29,6 +29,24 @@ bool IsMemPoolHeight(unsigned coinHeight) return coinHeight == CTxMemPoolEntry::MEMPOOL_HEIGHT; } +namespace +{ + +/** The UTXO hasher used in mempool logic. */ +class MempoolUtxoHasher : public TransactionUtxoHasher +{ + +public: + + uint256 GetUtxoHash(const CTransaction& tx) const override + { + return tx.GetHash(); + } + +}; + +} // anonymous namespace + CTxMemPool::CTxMemPool(const CFeeRate& _minRelayFee, const bool& addressIndex, const bool& spentIndex ): fSanityCheck(false) @@ -49,6 +67,8 @@ CTxMemPool::CTxMemPool(const CFeeRate& _minRelayFee, // Confirmation times for very-low-fee transactions that take more // than an hour or three to confirm are highly variable. // feePolicyEstimator = new CfeePolicyEstimator(25); + + utxoHasher.reset(new MempoolUtxoHasher()); } CTxMemPool::~CTxMemPool() @@ -112,6 +132,11 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry& entry, return true; } +const TransactionUtxoHasher& CTxMemPool::GetUtxoHasher() const +{ + return *utxoHasher; +} + void CTxMemPool::addAddressIndex(const CTxMemPoolEntry &entry, const CCoinsViewCache &view) { LOCK(cs); @@ -264,7 +289,7 @@ void CTxMemPool::remove(const CTransaction& origTx, std::list& rem // happen during chain re-orgs if origTx isn't re-accepted into // the mempool for any reason. for (unsigned int i = 0; i < origTx.vout.size(); i++) { - std::map::iterator it = mapNextTx.find(COutPoint(origTx.GetHash(), i)); + auto it = mapNextTx.find(COutPoint(utxoHasher->GetUtxoHash(origTx), i)); if (it == mapNextTx.end()) continue; txToRemove.push_back(it->second.ptx->GetHash()); @@ -284,7 +309,7 @@ void CTxMemPool::remove(const CTransaction& origTx, std::list& rem const CTransaction& tx = mempoolTx.GetTx(); if (fRecursive) { for (unsigned int i = 0; i < tx.vout.size(); i++) { - std::map::iterator it = mapNextTx.find(COutPoint(hash, i)); + std::map::iterator it = mapNextTx.find(COutPoint(utxoHasher->GetUtxoHash(tx), i)); if (it == mapNextTx.end()) continue; txToRemove.push_back(it->second.ptx->GetHash()); @@ -417,7 +442,7 @@ void CTxMemPool::check(const CCoinsViewCache* pcoins, const BlockMap& blockIndex CValidationState state; CTxUndo undo; assert(CheckInputs(tx, state, mempoolDuplicate, blockIndexMap, false, 0, false, NULL)); - UpdateCoinsWithTransaction(tx, mempoolDuplicate, undo, 1000000); + UpdateCoinsWithTransaction(tx, mempoolDuplicate, undo, *utxoHasher, 1000000); } } unsigned int stepsSinceLastRemove = 0; @@ -432,7 +457,7 @@ void CTxMemPool::check(const CCoinsViewCache* pcoins, const BlockMap& blockIndex } else { assert(CheckInputs(entry->GetTx(), state, mempoolDuplicate, blockIndexMap, false, 0, false, NULL)); CTxUndo undo; - UpdateCoinsWithTransaction(entry->GetTx(), mempoolDuplicate, undo, 1000000); + UpdateCoinsWithTransaction(entry->GetTx(), mempoolDuplicate, undo, *utxoHasher, 1000000); stepsSinceLastRemove = 0; } } @@ -479,9 +504,16 @@ bool CTxMemPool::lookupBareTxid(const uint256& btxid, CTransaction& result) cons bool CTxMemPool::lookupOutpoint(const uint256& hash, CTransaction& result) const { - /* For now (until we add the UTXO hasher and segwit light), the outpoint - is just the transaction ID. */ - return lookup(hash, result); + /* The TransactionUtxoHasher can only tell us the txid to use once we + know the transaction already. Thus we check both txid and bare txid + in our index; if one of them matches, we then cross-check with the + then-known transaction that it actually should hash to that UTXO. */ + if (lookup(hash, result) && utxoHasher->GetUtxoHash(result) == hash) + return true; + if (lookupBareTxid(hash, result) && utxoHasher->GetUtxoHash(result) == hash) + return true; + + return false; } CFeeRate CTxMemPool::estimateFee(int nBlocks) const @@ -612,4 +644,4 @@ bool SubmitTransactionToMempool(CTxMemPool& mempool, const CTransaction& tx) { CValidationState state; return SubmitTransactionToMempool(mempool,state,tx); -} \ No newline at end of file +} diff --git a/divi/src/txmempool.h b/divi/src/txmempool.h index 15be864b7..409fb8d28 100644 --- a/divi/src/txmempool.h +++ b/divi/src/txmempool.h @@ -8,6 +8,7 @@ #define BITCOIN_TXMEMPOOL_H #include +#include #include "addressindex.h" #include "spentindex.h" @@ -20,6 +21,7 @@ class BlockMap; class CAutoFile; +class TransactionUtxoHasher; /** Fake height value used in CCoins to signify they are only in the memory pool (since 0.8) */ bool IsMemPoolHeight(unsigned coinHeight); @@ -62,6 +64,7 @@ class CTxMemPool bool fSanityCheck; //! Normally false, true if -checkmempool or -regtest unsigned int nTransactionsUpdated; std::unique_ptr feePolicyEstimator; + std::unique_ptr utxoHasher; const CFeeRate& minRelayFee; //! Passed to constructor to avoid dependency on main uint64_t totalTxSize; //! sum of all mempool tx' byte sizes @@ -130,6 +133,9 @@ class CTxMemPool bool getSpentIndex(const CSpentIndexKey &key, CSpentIndexValue &value); + /** Returns the UTXO hasher instance used in the mempool. */ + const TransactionUtxoHasher& GetUtxoHasher() const; + /** Affect CreateNewBlock prioritisation of transactions */ bool IsPrioritizedTransaction(const uint256 hash); void PrioritiseTransaction(const uint256 hash, const CAmount nFeeDelta); From 397b6542385ae3626902ea777c7b81f9472bb792 Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Wed, 20 Jan 2021 16:53:02 +0100 Subject: [PATCH 02/14] Unit test for UpdateCoins UTXO creation. Add a unit test (together with the necessary framework around) for UTXO creation from UpdateCoins, based on the UTXO hasher (rather than the txid directly). --- divi/src/Makefile.test.include | 4 ++ divi/src/test/MockUtxoHasher.cpp | 21 ++++++++ divi/src/test/MockUtxoHasher.h | 39 ++++++++++++++ divi/src/test/TestCoinsView.cpp | 51 +++++++++++++++++++ divi/src/test/TestCoinsView.h | 19 +++++++ .../test/UtxoCheckingAndUpdating_tests.cpp | 45 ++++++++++++++++ 6 files changed, 179 insertions(+) create mode 100644 divi/src/test/MockUtxoHasher.cpp create mode 100644 divi/src/test/MockUtxoHasher.h create mode 100644 divi/src/test/TestCoinsView.cpp create mode 100644 divi/src/test/TestCoinsView.h create mode 100644 divi/src/test/UtxoCheckingAndUpdating_tests.cpp diff --git a/divi/src/Makefile.test.include b/divi/src/Makefile.test.include index d709768e4..9a1a76b83 100644 --- a/divi/src/Makefile.test.include +++ b/divi/src/Makefile.test.include @@ -64,6 +64,7 @@ BITCOIN_TESTS =\ test/MockBlockIncentivesPopulator.h \ test/MockBlockSubsidyProvider.h \ test/MockPoSStakeModifierService.h \ + test/MockUtxoHasher.cpp \ test/MockVaultManagerDatabase.h \ test/Monthlywalletbackupcreator_tests.cpp \ test/ActiveMasternode_tests.cpp \ @@ -85,6 +86,8 @@ BITCOIN_TESTS =\ test/SuperblockHelper_tests.cpp \ test/RandomCScriptGenerator.h \ test/RandomCScriptGenerator.cpp \ + test/TestCoinsView.h \ + test/TestCoinsView.cpp \ test/test_divi.cpp \ test/timedata_tests.cpp \ test/torcontrol_tests.cpp \ @@ -105,6 +108,7 @@ BITCOIN_TESTS =\ test/PoSTransactionCreator_tests.cpp \ test/LegacyPoSStakeModifierService_tests.cpp \ test/LotteryWinnersCalculatorTests.cpp \ + test/UtxoCheckingAndUpdating_tests.cpp \ test/VaultManager_tests.cpp \ test/multi_wallet_tests.cpp \ test/MockSignatureSizeEstimator.h \ diff --git a/divi/src/test/MockUtxoHasher.cpp b/divi/src/test/MockUtxoHasher.cpp new file mode 100644 index 000000000..70a5d1572 --- /dev/null +++ b/divi/src/test/MockUtxoHasher.cpp @@ -0,0 +1,21 @@ +#include "MockUtxoHasher.h" + +#include "hash.h" +#include "primitives/transaction.h" + +uint256 MockUtxoHasher::Add(const CTransaction& tx) +{ + ++cnt; + const uint256 value = Hash(&cnt, (&cnt) + 1); + customHashes.emplace(tx.GetHash(), value); + return value; +} + +uint256 MockUtxoHasher::GetUtxoHash(const CTransaction& tx) const +{ + const uint256 txid = tx.GetHash(); + const auto mit = customHashes.find(txid); + if (mit != customHashes.end()) + return mit->second; + return txid; +} diff --git a/divi/src/test/MockUtxoHasher.h b/divi/src/test/MockUtxoHasher.h new file mode 100644 index 000000000..d746dfadd --- /dev/null +++ b/divi/src/test/MockUtxoHasher.h @@ -0,0 +1,39 @@ +#ifndef MOCKUTXOHASHER_H +#define MOCKUTXOHASHER_H + +#include "UtxoCheckingAndUpdating.h" + +#include "uint256.h" + +#include + +class CTransaction; + +/** A TransactionUtxoHasher instance that returns normal txid's (as per before + * segwit-light) by default, but can be instructed to return custom hashes + * for certain transactions. */ +class MockUtxoHasher : public TransactionUtxoHasher +{ + +private: + + /** Custom hashes to return for given txid's. */ + std::map customHashes; + + /** Internal counter to produce unique fake hashes. */ + unsigned cnt = 0; + +public: + + MockUtxoHasher() + {} + + /** Marks the given transaction for returning a custom hash. The hash + * is generated uniquely and returned from the function. */ + uint256 Add(const CTransaction& tx); + + uint256 GetUtxoHash(const CTransaction& tx) const override; + +}; + +#endif // MOCKUTXOHASHER_H diff --git a/divi/src/test/TestCoinsView.cpp b/divi/src/test/TestCoinsView.cpp new file mode 100644 index 000000000..3aa992b45 --- /dev/null +++ b/divi/src/test/TestCoinsView.cpp @@ -0,0 +1,51 @@ +#include "TestCoinsView.h" + +namespace +{ + +/** An empty CCoinsView implementation. */ +class EmptyCoinsView : public CCoinsView +{ + +public: + + bool GetCoins(const uint256& txid, CCoins& coins) const override + { + return false; + } + + bool HaveCoins(const uint256& txid) const override + { + return false; + } + + uint256 GetBestBlock() const override + { + return uint256(0); + } + + bool BatchWrite(CCoinsMap& mapCoins, const uint256& hashBlock) override + { + return false; + } + + bool GetStats(CCoinsStats& stats) const override + { + return false; + } + + /** Returns a singleton instance that can be used in tests. Note that + * instances are always immutable (independent of their const state). */ + static EmptyCoinsView& Instance() + { + static EmptyCoinsView obj; + return obj; + } + +}; + +} // anonymous namespace + +TestCoinsView::TestCoinsView () + : CCoinsViewCache(&EmptyCoinsView::Instance()) +{} diff --git a/divi/src/test/TestCoinsView.h b/divi/src/test/TestCoinsView.h new file mode 100644 index 000000000..c35e1636c --- /dev/null +++ b/divi/src/test/TestCoinsView.h @@ -0,0 +1,19 @@ +#ifndef TESTCOINSVIEW_H +#define TESTCOINSVIEW_H + +#include "coins.h" + +/** A coins view for use in tests. It is a CoinsViewCache subclass that + * uses an "empty" coins view as its backing. Tests can modify it to insert + * coins as needed into the cache, and then access them. + */ +class TestCoinsView : public CCoinsViewCache +{ + +public: + + TestCoinsView(); + +}; + +#endif // TESTCOINSVIEW_H diff --git a/divi/src/test/UtxoCheckingAndUpdating_tests.cpp b/divi/src/test/UtxoCheckingAndUpdating_tests.cpp new file mode 100644 index 000000000..a7f0e9e9a --- /dev/null +++ b/divi/src/test/UtxoCheckingAndUpdating_tests.cpp @@ -0,0 +1,45 @@ +#include +#include + +#include "MockUtxoHasher.h" +#include "primitives/transaction.h" +#include "TestCoinsView.h" + +namespace +{ + +class UpdateCoinsTestFixture +{ + +protected: + + TestCoinsView coins; + MockUtxoHasher utxoHasher; + +}; + +BOOST_FIXTURE_TEST_SUITE(UpdateCoins_tests, UpdateCoinsTestFixture) + +BOOST_AUTO_TEST_CASE(addsCorrectOutputs) +{ + CMutableTransaction mtx; + mtx.vout.emplace_back(1, CScript() << OP_TRUE); + const CTransaction tx1(mtx); + + mtx.vout.clear(); + mtx.vout.emplace_back(2, CScript() << OP_TRUE); + const CTransaction tx2(mtx); + const auto id2 = utxoHasher.Add(tx2); + + CTxUndo txundo; + UpdateCoins(tx1, coins, txundo, utxoHasher, 101); + UpdateCoins(tx2, coins, txundo, utxoHasher, 102); + + BOOST_CHECK(coins.HaveCoins(tx1.GetHash())); + BOOST_CHECK(!coins.HaveCoins(tx2.GetHash())); + BOOST_CHECK(coins.HaveCoins(id2)); +} + +BOOST_AUTO_TEST_SUITE_END() + +} // anonymous namespace From 077523b23a8b2229f6cd66caf919a3507d73fdbd Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Fri, 18 Jun 2021 15:25:54 +0200 Subject: [PATCH 03/14] Unit test for mempool with UTXO hasher. This extends the mempool unit tests to explicitly verify that adding transactions, removing transactions, checking the pool and looking up coins / transactions still works even if we use the bare txid for some transactions as UTXO hash (as will be the case with segwit-light in the future). --- divi/src/test/MockUtxoHasher.cpp | 5 +++++ divi/src/test/MockUtxoHasher.h | 4 ++++ .../test/UtxoCheckingAndUpdating_tests.cpp | 4 ++-- divi/src/test/mempool_tests.cpp | 22 ++++++++++++------- divi/src/txmempool.cpp | 5 +++++ divi/src/txmempool.h | 4 ++++ 6 files changed, 34 insertions(+), 10 deletions(-) diff --git a/divi/src/test/MockUtxoHasher.cpp b/divi/src/test/MockUtxoHasher.cpp index 70a5d1572..2170dd638 100644 --- a/divi/src/test/MockUtxoHasher.cpp +++ b/divi/src/test/MockUtxoHasher.cpp @@ -11,6 +11,11 @@ uint256 MockUtxoHasher::Add(const CTransaction& tx) return value; } +void MockUtxoHasher::UseBareTxid(const CTransaction& tx) +{ + customHashes.emplace(tx.GetHash(), tx.GetBareTxid()); +} + uint256 MockUtxoHasher::GetUtxoHash(const CTransaction& tx) const { const uint256 txid = tx.GetHash(); diff --git a/divi/src/test/MockUtxoHasher.h b/divi/src/test/MockUtxoHasher.h index d746dfadd..9ea8f0ade 100644 --- a/divi/src/test/MockUtxoHasher.h +++ b/divi/src/test/MockUtxoHasher.h @@ -32,6 +32,10 @@ class MockUtxoHasher : public TransactionUtxoHasher * is generated uniquely and returned from the function. */ uint256 Add(const CTransaction& tx); + /** Marks the given transaction for using the bare txid rather than + * the normal txid. */ + void UseBareTxid(const CTransaction& tx); + uint256 GetUtxoHash(const CTransaction& tx) const override; }; diff --git a/divi/src/test/UtxoCheckingAndUpdating_tests.cpp b/divi/src/test/UtxoCheckingAndUpdating_tests.cpp index a7f0e9e9a..a185fe470 100644 --- a/divi/src/test/UtxoCheckingAndUpdating_tests.cpp +++ b/divi/src/test/UtxoCheckingAndUpdating_tests.cpp @@ -32,8 +32,8 @@ BOOST_AUTO_TEST_CASE(addsCorrectOutputs) const auto id2 = utxoHasher.Add(tx2); CTxUndo txundo; - UpdateCoins(tx1, coins, txundo, utxoHasher, 101); - UpdateCoins(tx2, coins, txundo, utxoHasher, 102); + UpdateCoinsWithTransaction(tx1, coins, txundo, utxoHasher, 101); + UpdateCoinsWithTransaction(tx2, coins, txundo, utxoHasher, 102); BOOST_CHECK(coins.HaveCoins(tx1.GetHash())); BOOST_CHECK(!coins.HaveCoins(tx2.GetHash())); diff --git a/divi/src/test/mempool_tests.cpp b/divi/src/test/mempool_tests.cpp index 7a082bd51..36ec62ebb 100644 --- a/divi/src/test/mempool_tests.cpp +++ b/divi/src/test/mempool_tests.cpp @@ -5,6 +5,7 @@ #include "txmempool.h" #include "FakeBlockIndexChain.h" +#include "MockUtxoHasher.h" #include #include @@ -20,7 +21,8 @@ class MempoolTestFixture /** A parent transaction. */ CMutableTransaction txParent; - /** Three children of the parent. */ + /** Three children of the parent. They use the bare txid for their UTXOs + * in our UTXO hasher. */ CMutableTransaction txChild[3]; /** Three grand children. */ @@ -45,6 +47,8 @@ class MempoolTestFixture testPool(CFeeRate(0), addressIndex, spentIndex), coinsMemPool(nullptr, testPool), coins(&coinsMemPool) { + std::unique_ptr utxoHasher(new MockUtxoHasher()); + CMutableTransaction mtx; mtx.vout.emplace_back(2 * COIN, CScript () << OP_TRUE); mtx.vout.emplace_back(COIN, CScript () << OP_TRUE); @@ -76,13 +80,14 @@ class MempoolTestFixture txChild[i].vout.resize(1); txChild[i].vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; txChild[i].vout[0].nValue = COIN; + utxoHasher->UseBareTxid(txChild[i]); } for (int i = 0; i < 3; i++) { txGrandChild[i].vin.resize(1); txGrandChild[i].vin[0].scriptSig = CScript() << OP_11; - txGrandChild[i].vin[0].prevout.hash = txChild[i].GetHash(); + txGrandChild[i].vin[0].prevout.hash = txChild[i].GetBareTxid(); txGrandChild[i].vin[0].prevout.n = 0; txGrandChild[i].vout.resize(1); txGrandChild[i].vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; @@ -90,6 +95,7 @@ class MempoolTestFixture } testPool.setSanityCheck(true); + testPool.SetUtxoHasherForTesting(std::move(utxoHasher)); testPool.clear(); } @@ -192,18 +198,18 @@ BOOST_AUTO_TEST_CASE(MempoolOutpointLookup) BOOST_CHECK(testPool.lookupOutpoint(txParent.GetHash(), tx)); BOOST_CHECK(!testPool.lookupOutpoint(txParent.GetBareTxid(), tx)); - BOOST_CHECK(testPool.lookupOutpoint(txChild[0].GetHash(), tx)); - BOOST_CHECK(!testPool.lookupOutpoint(txChild[0].GetBareTxid(), tx)); + BOOST_CHECK(!testPool.lookupOutpoint(txChild[0].GetHash(), tx)); + BOOST_CHECK(testPool.lookupOutpoint(txChild[0].GetBareTxid(), tx)); BOOST_CHECK(viewPool.HaveCoins(txParent.GetHash())); BOOST_CHECK(viewPool.GetCoins(txParent.GetHash(), c)); BOOST_CHECK(!viewPool.HaveCoins(txParent.GetBareTxid())); BOOST_CHECK(!viewPool.GetCoins(txParent.GetBareTxid(), c)); - BOOST_CHECK(viewPool.HaveCoins(txChild[0].GetHash())); - BOOST_CHECK(viewPool.GetCoins(txChild[0].GetHash(), c)); - BOOST_CHECK(!viewPool.HaveCoins(txChild[0].GetBareTxid())); - BOOST_CHECK(!viewPool.GetCoins(txChild[0].GetBareTxid(), c)); + BOOST_CHECK(!viewPool.HaveCoins(txChild[0].GetHash())); + BOOST_CHECK(!viewPool.GetCoins(txChild[0].GetHash(), c)); + BOOST_CHECK(viewPool.HaveCoins(txChild[0].GetBareTxid())); + BOOST_CHECK(viewPool.GetCoins(txChild[0].GetBareTxid(), c)); } BOOST_AUTO_TEST_CASE(MempoolExists) diff --git a/divi/src/txmempool.cpp b/divi/src/txmempool.cpp index 98fcfe16f..c4f329aa8 100644 --- a/divi/src/txmempool.cpp +++ b/divi/src/txmempool.cpp @@ -137,6 +137,11 @@ const TransactionUtxoHasher& CTxMemPool::GetUtxoHasher() const return *utxoHasher; } +void CTxMemPool::SetUtxoHasherForTesting(std::unique_ptr hasher) +{ + utxoHasher = std::move(hasher); +} + void CTxMemPool::addAddressIndex(const CTxMemPoolEntry &entry, const CCoinsViewCache &view) { LOCK(cs); diff --git a/divi/src/txmempool.h b/divi/src/txmempool.h index 409fb8d28..f2a288540 100644 --- a/divi/src/txmempool.h +++ b/divi/src/txmempool.h @@ -136,6 +136,10 @@ class CTxMemPool /** Returns the UTXO hasher instance used in the mempool. */ const TransactionUtxoHasher& GetUtxoHasher() const; + /** Replaces the UTXO hasher used in the mempool with the given instance, + * which allows dependency injection for unit tests. */ + void SetUtxoHasherForTesting(std::unique_ptr hasher); + /** Affect CreateNewBlock prioritisation of transactions */ bool IsPrioritizedTransaction(const uint256 hash); void PrioritiseTransaction(const uint256 hash, const CAmount nFeeDelta); From 9d99a87d09cb0008d346fc5d38d8e650cbaa2a35 Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Thu, 2 Sep 2021 14:35:41 +0200 Subject: [PATCH 04/14] Use UTXO hasher for spending coins. Use the UTXO hasher abstraction in the wallet and for staking (i.e. for places where coins are spent). The wallet gets its own instance, which will allow for dependency injection in tests. For now, the hasher used in the wallet is just the normal hasher, i.e. there are no actual changes in behaviour. In the future, the wallet hasher can be changed accordingly for the activation of segwit light. --- divi/src/CoinControlSelectionAlgorithm.cpp | 10 +++-- divi/src/CoinControlSelectionAlgorithm.h | 9 +++-- divi/src/I_StakingCoinSelector.h | 8 +++- divi/src/PoSTransactionCreator.cpp | 2 +- divi/src/StakableCoin.h | 6 +-- divi/src/rpcmasternode.cpp | 2 +- divi/src/rpcrawtransaction.cpp | 4 ++ divi/src/wallet.cpp | 44 ++++++++++++++++++---- divi/src/wallet.h | 6 +++ divi/src/walletdustcombiner.cpp | 6 +-- 10 files changed, 73 insertions(+), 24 deletions(-) diff --git a/divi/src/CoinControlSelectionAlgorithm.cpp b/divi/src/CoinControlSelectionAlgorithm.cpp index 67f510ad0..3312e4aa8 100644 --- a/divi/src/CoinControlSelectionAlgorithm.cpp +++ b/divi/src/CoinControlSelectionAlgorithm.cpp @@ -1,10 +1,12 @@ #include #include +#include #include CoinControlSelectionAlgorithm::CoinControlSelectionAlgorithm( - const CCoinControl* coinControl - ): coinControl_(coinControl) + const CCoinControl* coinControl, + const CWallet& wallet + ): coinControl_(coinControl), wallet_(wallet) { } @@ -20,7 +22,7 @@ std::set CoinControlSelectionAlgorithm::SelectCoins( for(const COutput& out: vCoins) { if (!out.fSpendable || - (!coinControl_->fAllowOtherInputs && !coinControl_->IsSelected(out.tx->GetHash(),out.i))) + (!coinControl_->fAllowOtherInputs && !coinControl_->IsSelected(wallet_.GetUtxoHash(*out.tx),out.i))) { continue; } @@ -31,4 +33,4 @@ std::set CoinControlSelectionAlgorithm::SelectCoins( } fees = totalInputs - transactionToSelectCoinsFor.GetValueOut(); return setCoinsRet; -} \ No newline at end of file +} diff --git a/divi/src/CoinControlSelectionAlgorithm.h b/divi/src/CoinControlSelectionAlgorithm.h index 8b514bf50..97453bc23 100644 --- a/divi/src/CoinControlSelectionAlgorithm.h +++ b/divi/src/CoinControlSelectionAlgorithm.h @@ -2,16 +2,19 @@ #define COIN_CONTROL_COIN_SELECTOR_H #include class CCoinControl; +class CWallet; class CoinControlSelectionAlgorithm: public I_CoinSelectionAlgorithm { private: const CCoinControl* coinControl_; + const CWallet& wallet_; public: - CoinControlSelectionAlgorithm( - const CCoinControl* coinControl); + explicit CoinControlSelectionAlgorithm( + const CCoinControl* coinControl, + const CWallet& wallet); virtual std::set SelectCoins( const CMutableTransaction& transactionToSelectCoinsFor, const std::vector& vCoins, CAmount& fees) const; }; -#endif// COIN_CONTROL_COIN_SELECTOR_H \ No newline at end of file +#endif// COIN_CONTROL_COIN_SELECTOR_H diff --git a/divi/src/I_StakingCoinSelector.h b/divi/src/I_StakingCoinSelector.h index fbe7c3181..d8f427c19 100644 --- a/divi/src/I_StakingCoinSelector.h +++ b/divi/src/I_StakingCoinSelector.h @@ -5,6 +5,8 @@ #include #include +class CMerkleTx; + class I_StakingCoinSelector { public: @@ -17,5 +19,9 @@ class I_StakingWallet: public virtual CKeyStore, public I_StakingCoinSelector, p { public: virtual ~I_StakingWallet(){} + + /** Returns the UTXO hash that should be used for spending outputs + * from the given transaction (which should be part of the wallet). */ + virtual uint256 GetUtxoHash(const CMerkleTx& tx) const = 0; }; -#endif// I_STAKING_COIN_SELECTOR_H \ No newline at end of file +#endif// I_STAKING_COIN_SELECTOR_H diff --git a/divi/src/PoSTransactionCreator.cpp b/divi/src/PoSTransactionCreator.cpp index 129f14275..2022bf38c 100644 --- a/divi/src/PoSTransactionCreator.cpp +++ b/divi/src/PoSTransactionCreator.cpp @@ -146,7 +146,7 @@ void PoSTransactionCreator::CombineUtxos( for(const StakableCoin& pcoin : stakedCoins_->asSet()) { if(pcoin.GetTxOut().scriptPubKey == txNew.vout[1].scriptPubKey && - pcoin.tx->GetHash() != txNew.vin[0].prevout.hash) + wallet_.GetUtxoHash(*pcoin.tx) != txNew.vin[0].prevout.hash) { if(pcoin.GetTxOut().nValue + nCredit > nCombineThreshold) continue; diff --git a/divi/src/StakableCoin.h b/divi/src/StakableCoin.h index f5877febe..9803a7bcd 100644 --- a/divi/src/StakableCoin.h +++ b/divi/src/StakableCoin.h @@ -1,15 +1,15 @@ #ifndef STAKABLE_COIN_H #define STAKABLE_COIN_H #include -#include +#include struct StakableCoin { - const CTransaction* tx; + const CMerkleTx* tx; COutPoint utxo; uint256 blockHashOfFirstConfirmation; explicit StakableCoin( - const CTransaction& txIn, + const CMerkleTx& txIn, const COutPoint& utxoIn, uint256 blockHashIn ): tx(&txIn) diff --git a/divi/src/rpcmasternode.cpp b/divi/src/rpcmasternode.cpp index 191956cf8..cf406e67e 100644 --- a/divi/src/rpcmasternode.cpp +++ b/divi/src/rpcmasternode.cpp @@ -104,7 +104,7 @@ Value allocatefunds(const Array& params, bool fHelp) SendMoneyToAddress(acctAddr.Get(), CMasternode::GetTierCollateralAmount(nMasternodeTier), wtx); Object obj; - obj.push_back(Pair("txhash", wtx.GetHash().GetHex())); + obj.push_back(Pair("txhash", pwalletMain->GetUtxoHash(wtx).GetHex())); bool found = false; auto nAmount = CMasternode::GetTierCollateralAmount(nMasternodeTier); for(size_t i = 0; i < wtx.vout.size(); ++i) diff --git a/divi/src/rpcrawtransaction.cpp b/divi/src/rpcrawtransaction.cpp index ac8ed4f9d..e53ea5099 100644 --- a/divi/src/rpcrawtransaction.cpp +++ b/divi/src/rpcrawtransaction.cpp @@ -343,6 +343,8 @@ Value listunspent(const Array& params, bool fHelp) "[ (array of json object)\n" " {\n" " \"txid\" : \"txid\", (string) the transaction id \n" + " \"baretxid\" : \"baretxid\", (string) The bare txid (without signatures)\n" + " \"outputhash\" : \"outputhash\", (string) The hash (txid or bare txid) that should be used for spending\n" " \"vout\" : n, (numeric) the vout value\n" " \"address\" : \"address\", (string) the divi address\n" " \"account\" : \"account\", (string) The associated account, or \"\" for the default account\n" @@ -401,6 +403,8 @@ Value listunspent(const Array& params, bool fHelp) const CScript& pk = out.tx->vout[out.i].scriptPubKey; Object entry; entry.push_back(Pair("txid", out.tx->GetHash().GetHex())); + entry.push_back(Pair("baretxid", out.tx->GetBareTxid().GetHex())); + entry.push_back(Pair("outputhash", pwalletMain->GetUtxoHash(*out.tx).GetHex())); entry.push_back(Pair("vout", out.i)); CTxDestination address; if (ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) { diff --git a/divi/src/wallet.cpp b/divi/src/wallet.cpp index 67dd08542..4bf8c104c 100644 --- a/divi/src/wallet.cpp +++ b/divi/src/wallet.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -172,6 +173,25 @@ std::set AddressBookManager::GetAccountAddresses(std::string str return result; } +namespace +{ + +/** Dummy UTXO hasher for the wallet. For now, this just always returns + * the normal txid, but we will later change it to return the proper hash + * for a WalletTx. */ +class WalletUtxoHasher : public TransactionUtxoHasher +{ + +public: + + uint256 GetUtxoHash(const CTransaction& tx) const override + { + return tx.GetHash(); + } + +}; + +} // anonymous namespace CWallet::CWallet(const CChain& chain, const BlockMap& blockMap ): cs_wallet() @@ -191,6 +211,7 @@ CWallet::CWallet(const CChain& chain, const BlockMap& blockMap , transactionRecord_(new WalletTransactionRecord(cs_wallet,strWalletFile) ) , outputTracker_( new SpentOutputTracker(*transactionRecord_,*confirmationNumberCalculator_) ) , pwalletdbEncryption() + , utxoHasher(new WalletUtxoHasher() ) , nWalletVersion(FEATURE_BASE) , nWalletMaxVersion(FEATURE_BASE) , mapKeyMetadata() @@ -483,7 +504,7 @@ CAmount CWallet::ComputeCredit(const CWalletTx& tx, const UtxoOwnershipFilter& f { const CAmount maxMoneyAllowedInOutput = Params().MaxMoneyOut(); CAmount nCredit = 0; - uint256 hash = tx.GetHash(); + const uint256 hash = GetUtxoHash(tx); for (unsigned int i = 0; i < tx.vout.size(); i++) { if( (creditFilterFlags & REQUIRE_UNSPENT) && IsSpent(tx,i)) continue; if( (creditFilterFlags & REQUIRE_UNLOCKED) && IsLockedCoin(hash,i)) continue; @@ -1100,7 +1121,7 @@ std::set CWallet::GetConflicts(const uint256& txid) const */ bool CWallet::IsSpent(const CWalletTx& wtx, unsigned int n) const { - return outputTracker_->IsSpent(wtx.GetHash(), n); + return outputTracker_->IsSpent(GetUtxoHash(wtx), n); } bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) @@ -1694,7 +1715,7 @@ bool CWallet::IsAvailableForSpending( return false; } - const uint256 hash = pcoin->GetHash(); + const uint256 hash = GetUtxoHash(*pcoin); if (IsSpent(*pcoin, i)) return false; @@ -1865,7 +1886,7 @@ bool CWallet::SelectStakeCoins(std::set& setCoins) const continue; //add to our stake set - setCoins.emplace(*out.tx, COutPoint(out.tx->GetHash(), out.i), out.tx->hashBlock); + setCoins.emplace(*out.tx, COutPoint(GetUtxoHash(*out.tx), out.i), out.tx->hashBlock); nAmountSelected += out.tx->vout[out.i].nValue; } return true; @@ -2150,6 +2171,7 @@ static bool SetChangeOutput( } static CAmount AttachInputs( + const TransactionUtxoHasher& utxoHasher, const std::set& setCoins, CMutableTransaction& txWithoutChange) { @@ -2157,7 +2179,7 @@ static CAmount AttachInputs( CAmount nValueIn = 0; for(const COutput& coin: setCoins) { - txWithoutChange.vin.emplace_back(coin.tx->GetHash(), coin.i); + txWithoutChange.vin.emplace_back(utxoHasher.GetUtxoHash(*coin.tx), coin.i); nValueIn += coin.Value(); } return nValueIn; @@ -2166,6 +2188,7 @@ static CAmount AttachInputs( static std::pair SelectInputsProvideSignaturesAndFees( const CKeyStore& walletKeyStore, const I_CoinSelectionAlgorithm* coinSelector, + const TransactionUtxoHasher& utxoHasher, const std::vector& vCoins, const CTxMemPool& mempool, CMutableTransaction& txNew, @@ -2182,7 +2205,7 @@ static std::pair SelectInputsProvideSignaturesAndFees( txNew.vin.clear(); // Choose coins to use std::set setCoins = coinSelector->SelectCoins(txNew,vCoins,nFeeRet); - CAmount nValueIn = AttachInputs(setCoins,txNew); + CAmount nValueIn = AttachInputs(utxoHasher, setCoins, txNew); CAmount nTotalValue = totalValueToSend + nFeeRet; if (setCoins.empty() || nValueIn < nTotalValue) { @@ -2244,7 +2267,7 @@ std::pair CWallet::CreateTransaction( { return {translate("Transaction output(s) amount too small"),false}; } - return SelectInputsProvideSignaturesAndFees(*this, coinSelector,vCoins,mempool,txNew,reservekey,wtxNew); + return SelectInputsProvideSignaturesAndFees(*this, coinSelector, *utxoHasher, vCoins, mempool, txNew, reservekey, wtxNew); } /** @@ -2319,6 +2342,11 @@ std::pair CWallet::SendMoney( return {std::string(""),true}; } +uint256 CWallet::GetUtxoHash(const CMerkleTx& tx) const +{ + return utxoHasher->GetUtxoHash(tx); +} + DBErrors CWallet::LoadWallet() { if (!fFileBacked) @@ -3030,4 +3058,4 @@ void CWallet::LockFully() LOCK(cs_wallet); walletStakingOnly = false; Lock(); -} \ No newline at end of file +} diff --git a/divi/src/wallet.h b/divi/src/wallet.h index 350b5099e..9b36f831d 100644 --- a/divi/src/wallet.h +++ b/divi/src/wallet.h @@ -29,6 +29,8 @@ #include #include +#include + class I_CoinSelectionAlgorithm; class CKeyMetadata; class CKey; @@ -39,6 +41,7 @@ class CBlockIndex; struct StakableCoin; class WalletTransactionRecord; class SpentOutputTracker; +class TransactionUtxoHasher; class BlockMap; class CChain; class CCoinControl; @@ -159,6 +162,7 @@ class CWallet : std::unique_ptr transactionRecord_; std::unique_ptr outputTracker_; std::unique_ptr pwalletdbEncryption; + std::unique_ptr utxoHasher; int nWalletVersion; //! the current wallet version: clients below this version are not able to load the wallet int nWalletMaxVersion;//! the maximum wallet format version: memory-only variable that specifies to what version this wallet may be upgraded @@ -287,6 +291,8 @@ class CWallet : void UnlockAllCoins(); void ListLockedCoins(CoinVector& vOutpts); + uint256 GetUtxoHash(const CMerkleTx& tx) const override; + // keystore implementation // Generate a new key CPubKey GenerateNewKey(uint32_t nAccountIndex, bool fInternal); diff --git a/divi/src/walletdustcombiner.cpp b/divi/src/walletdustcombiner.cpp index a176b88d1..c02e3231d 100644 --- a/divi/src/walletdustcombiner.cpp +++ b/divi/src/walletdustcombiner.cpp @@ -71,7 +71,7 @@ void WalletDustCombiner::CombineDust(CAmount combineThreshold) if (out.Value() > combineThreshold * COIN) continue; - COutPoint outpt(out.tx->GetHash(), out.i); + COutPoint outpt(wallet_.GetUtxoHash(*out.tx), out.i); coinControl->Select(outpt); coinsToCombine.push_back(out); nTotalRewardsValue += out.Value(); @@ -94,7 +94,7 @@ void WalletDustCombiner::CombineDust(CAmount combineThreshold) CWalletTx wtx; std::pair txCreationResult; { - CoinControlSelectionAlgorithm coinSelectionAlgorithm(coinControl.get()); + CoinControlSelectionAlgorithm coinSelectionAlgorithm(coinControl.get(), wallet_); txCreationResult = wallet_.SendMoney(vecSend, wtx, &coinSelectionAlgorithm, ALL_SPENDABLE_COINS); } if (!txCreationResult.second) { @@ -119,4 +119,4 @@ void combineWalletDust(const Settings& settings) settings.GetArg("-combinethreshold",std::numeric_limits::max() ) ); } } -} \ No newline at end of file +} From 6a82d9704a7a2bc25b75fdd2244dec9bb26c216a Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Fri, 18 Jun 2021 15:44:50 +0200 Subject: [PATCH 05/14] Unit test UTXO hasher in PoSTransactionCreator. Extend the unit tests for PoSTransactionCreator to verify that the UTXO hasher is used to build up the PoS transaction when staking. --- divi/src/test/PoSTransactionCreator_tests.cpp | 23 +++++++++++++++++++ divi/src/wallet.cpp | 5 ++++ divi/src/wallet.h | 3 +++ 3 files changed, 31 insertions(+) diff --git a/divi/src/test/PoSTransactionCreator_tests.cpp b/divi/src/test/PoSTransactionCreator_tests.cpp index 36a1dd00a..109b9405f 100644 --- a/divi/src/test/PoSTransactionCreator_tests.cpp +++ b/divi/src/test/PoSTransactionCreator_tests.cpp @@ -15,6 +15,7 @@ #include "test/FakeWallet.h" #include "test/MockBlockIncentivesPopulator.h" #include "test/MockBlockSubsidyProvider.h" +#include "test/MockUtxoHasher.h" #include #include "test/test_only.h" @@ -57,6 +58,10 @@ class PoSTransactionCreatorTestFixture /** A script from the wallet for convenience. */ const CScript walletScript; + /** UTXO hasher used in the wallet. The instance is owned by the wallet, + * and a reference is kept here. */ + MockUtxoHasher* utxoHasher; + /* Convenience variables for tests to use in calls to CreatePoS. */ CMutableTransaction mtx; unsigned txTime; @@ -74,6 +79,10 @@ class PoSTransactionCreatorTestFixture posModule.proofOfStakeGenerator(), wallet, hashedBlockTimestamps), walletScript(GetScriptForDestination(wallet.getNewKey().GetID())) { + std::unique_ptr hasher(new MockUtxoHasher()); + utxoHasher = hasher.get(); + wallet.SetUtxoHasherForTesting(std::move (hasher)); + /* Set up a default block reward if we don't need anything else. */ EXPECT_CALL(blockSubsidyProvider, GetFullBlockValue(_)) .WillRepeatedly(Return(11 * COIN)); @@ -120,6 +129,20 @@ BOOST_AUTO_TEST_CASE(checksForConfirmationsAndAge) BOOST_CHECK(CreatePoS()); } +BOOST_AUTO_TEST_CASE(usesUtxoHashForInputs) +{ + const auto& tx = wallet.AddDefaultTx(walletScript, outputIndex, 1000 * COIN); + wallet.FakeAddToChain(tx); + wallet.AddConfirmations(20, 1000); + + const auto hash = utxoHasher->Add(tx); + BOOST_CHECK(CreatePoS()); + + BOOST_CHECK_EQUAL(mtx.vin.size(), 1); + BOOST_CHECK_EQUAL(mtx.vin[0].prevout.n, outputIndex); + BOOST_CHECK(mtx.vin[0].prevout.hash == hash); +} + BOOST_AUTO_TEST_SUITE_END() } // anonymous namespace diff --git a/divi/src/wallet.cpp b/divi/src/wallet.cpp index 4bf8c104c..45aa1abba 100644 --- a/divi/src/wallet.cpp +++ b/divi/src/wallet.cpp @@ -2347,6 +2347,11 @@ uint256 CWallet::GetUtxoHash(const CMerkleTx& tx) const return utxoHasher->GetUtxoHash(tx); } +void CWallet::SetUtxoHasherForTesting(std::unique_ptr hasher) +{ + utxoHasher = std::move(hasher); +} + DBErrors CWallet::LoadWallet() { if (!fFileBacked) diff --git a/divi/src/wallet.h b/divi/src/wallet.h index 9b36f831d..cc7f6e821 100644 --- a/divi/src/wallet.h +++ b/divi/src/wallet.h @@ -293,6 +293,9 @@ class CWallet : uint256 GetUtxoHash(const CMerkleTx& tx) const override; + /** Replaces the UTXO hasher used in the wallet, for testing purposes. */ + void SetUtxoHasherForTesting(std::unique_ptr hasher); + // keystore implementation // Generate a new key CPubKey GenerateNewKey(uint32_t nAccountIndex, bool fInternal); From cccae189105aaaa0891fe4a60bad0bc3eb9e77bd Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Tue, 9 Feb 2021 15:41:24 +0100 Subject: [PATCH 06/14] Unit tests for UTXO hasher in wallet. Extend the unit tests in wallet_coinmanagement_tests.cpp to include also explicit checks for situations in which the wallet is supposed to the UTXO hasher rather than e.g. a plain transaction hash. --- divi/src/test/FakeWallet.cpp | 10 ++- divi/src/test/FakeWallet.h | 3 + divi/src/test/wallet_coinmanagement_tests.cpp | 82 +++++++++++++++++++ 3 files changed, 92 insertions(+), 3 deletions(-) diff --git a/divi/src/test/FakeWallet.cpp b/divi/src/test/FakeWallet.cpp index a171537e3..98ae035db 100644 --- a/divi/src/test/FakeWallet.cpp +++ b/divi/src/test/FakeWallet.cpp @@ -116,10 +116,8 @@ void FakeWallet::AddConfirmations(const unsigned numConf, const int64_t minAge) fakeChain.addBlocks(numConf, version, fakeChain.activeChain->Tip()->nTime + minAge); } -const CWalletTx& FakeWallet::AddDefaultTx(const CScript& scriptToPayTo, unsigned& outputIndex, - const CAmount amount) +const CWalletTx& FakeWallet::Add(const CTransaction& tx) { - const CTransaction tx = createDefaultTransaction(scriptToPayTo, outputIndex, amount); CWalletTx wtx(tx); AddToWallet(wtx); const CWalletTx* txPtr = GetWalletTx(wtx.GetHash()); @@ -127,6 +125,12 @@ const CWalletTx& FakeWallet::AddDefaultTx(const CScript& scriptToPayTo, unsigned return *txPtr; } +const CWalletTx& FakeWallet::AddDefaultTx(const CScript& scriptToPayTo, unsigned& outputIndex, + const CAmount amount) +{ + return Add(createDefaultTransaction(scriptToPayTo, outputIndex, amount)); +} + void FakeWallet::FakeAddToChain(const CWalletTx& tx) { auto* txPtr = const_cast(&tx); diff --git a/divi/src/test/FakeWallet.h b/divi/src/test/FakeWallet.h index 70c581548..ffb1ea39d 100644 --- a/divi/src/test/FakeWallet.h +++ b/divi/src/test/FakeWallet.h @@ -45,6 +45,9 @@ class FakeWallet : public CWallet * and number of confirmations. */ void AddConfirmations(unsigned numConf, int64_t minAge = 0); + /** Adds a given transaction to the wallet. */ + const CWalletTx& Add(const CTransaction& tx); + /** Adds a new ordinary transaction to the wallet, paying a given amount * to a given script. The transaction is returned, and the output index * with the output to the requested script is set. */ diff --git a/divi/src/test/wallet_coinmanagement_tests.cpp b/divi/src/test/wallet_coinmanagement_tests.cpp index f84394200..0de8121ed 100644 --- a/divi/src/test/wallet_coinmanagement_tests.cpp +++ b/divi/src/test/wallet_coinmanagement_tests.cpp @@ -15,7 +15,9 @@ #include #include #include +#include #include +#include #include class WalletCoinManagementTestFixture @@ -23,11 +25,16 @@ class WalletCoinManagementTestFixture public: FakeBlockIndexWithHashes fakeChain; FakeWallet wallet; + MockUtxoHasher* utxoHasher; WalletCoinManagementTestFixture() : fakeChain(1, 1600000000, 1), wallet(fakeChain) { ENTER_CRITICAL_SECTION(wallet.cs_wallet); + + std::unique_ptr hasher(new MockUtxoHasher()); + utxoHasher = hasher.get(); + wallet.SetUtxoHasherForTesting(std::move(hasher)); } ~WalletCoinManagementTestFixture() { @@ -401,4 +408,79 @@ BOOST_AUTO_TEST_CASE(willEnsureStakingBalanceAndTotalBalanceAgreeEvenIfTxsBelong BOOST_CHECK_EQUAL_MESSAGE(stakableCoins.size(),2,"Missing coins in the stakable set"); } +BOOST_AUTO_TEST_CASE(willUseUtxoHasherForCoinLock) +{ + CScript defaultScript = GetScriptForDestination(wallet.getNewKey().GetID()); + unsigned outputIndex=0; + const CWalletTx& wtx = wallet.AddDefaultTx(defaultScript,outputIndex, COIN); + const auto id = utxoHasher->Add(wtx); + + wallet.LockCoin(COutPoint(id, outputIndex)); + + bool fIsSpendable; + BOOST_CHECK(!wallet.IsAvailableForSpending(&wtx,outputIndex,false,fIsSpendable,ALL_SPENDABLE_COINS)); + + BOOST_CHECK_EQUAL(wallet.ComputeCredit(wtx, isminetype::ISMINE_SPENDABLE, REQUIRE_UNLOCKED), 0); + BOOST_CHECK_EQUAL(wallet.ComputeCredit(wtx, isminetype::ISMINE_SPENDABLE, REQUIRE_LOCKED), COIN); +} + +BOOST_AUTO_TEST_CASE(willMarkOutputsSpentBasedOnUtxoHasher) +{ + CScript defaultScript = GetScriptForDestination(wallet.getNewKey().GetID()); + unsigned outputIndex=0; + const CWalletTx& wtx = wallet.AddDefaultTx(defaultScript,outputIndex, COIN); + const auto id = utxoHasher->Add(wtx); + + CMutableTransaction mtx; + mtx.vin.emplace_back(id, outputIndex); + const auto& spendTx = wallet.Add(mtx); + + /* The wallet's IsSpent check verifies also the spending tx' number + of confirmations, which requires it to be either in the chain or + in the mempool at least. */ + wallet.FakeAddToChain(wtx); + wallet.FakeAddToChain(spendTx); + + BOOST_CHECK(wallet.IsSpent(wtx, outputIndex)); + bool fIsSpendable; + BOOST_CHECK(!wallet.IsAvailableForSpending(&wtx,outputIndex,false,fIsSpendable,ALL_SPENDABLE_COINS)); +} + +/** Fake coin selector that just uses all available outputs. */ +class AllCoinSelector : public I_CoinSelectionAlgorithm +{ +public: + + AllCoinSelector() = default; + + std::set SelectCoins(const CMutableTransaction& tx, const std::vector& vCoins, CAmount& fees) const override + { + fees = COIN / 100; + return std::set(vCoins.begin(), vCoins.end()); + } + +}; + +BOOST_AUTO_TEST_CASE(willUseUtxoHashForSpendingCoins) +{ + CScript defaultScript = GetScriptForDestination(wallet.getNewKey().GetID()); + unsigned outputIndex=0; + const CWalletTx& wtx = wallet.AddDefaultTx(defaultScript,outputIndex, COIN); + const auto id = utxoHasher->Add(wtx); + wallet.FakeAddToChain(wtx); + + const CScript sendTo = CScript() << OP_TRUE; + std::string strError; + CReserveKey reserveKey(wallet); + CAmount nFeeRequired; + CWalletTx wtxNew; + AllCoinSelector coinSelector; + BOOST_CHECK(wallet.CreateTransaction({{sendTo, COIN / 10}}, wtxNew, reserveKey, &coinSelector).second); + + BOOST_CHECK_EQUAL(wtxNew.vin.size(), 1); + const auto& prevout = wtxNew.vin[0].prevout; + BOOST_CHECK(prevout.hash == id); + BOOST_CHECK_EQUAL(prevout.n, outputIndex); +} + BOOST_AUTO_TEST_SUITE_END() From 8c3a969edca44151abe6280db023515aeeaea99d Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Wed, 3 Nov 2021 15:42:12 +0100 Subject: [PATCH 07/14] Use UTXO hasher in VaultManager. The VaultManager class needs a TransactionUtxoHasher instance so that it can properly track spent outputs with the outputTracker_. This adds a reference to the UTXO hasher to the instance and uses it in the proper place. --- divi/src/VaultManager.cpp | 5 ++++- divi/src/VaultManager.h | 3 +++ divi/src/test/VaultManager_tests.cpp | 20 +++++++++++--------- divi/src/wallet.cpp | 2 +- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/divi/src/VaultManager.cpp b/divi/src/VaultManager.cpp index 833f2405f..f96dd557a 100644 --- a/divi/src/VaultManager.cpp +++ b/divi/src/VaultManager.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -13,8 +14,10 @@ constexpr const char* VAULT_DEPOSIT_DESCRIPTION = "isVaultDeposit"; VaultManager::VaultManager( const I_MerkleTxConfirmationNumberCalculator& confirmationsCalculator, + const TransactionUtxoHasher& utxoHasher, I_VaultManagerDatabase& vaultManagerDB ): confirmationsCalculator_(confirmationsCalculator) + , utxoHasher_(utxoHasher) , vaultManagerDB_(vaultManagerDB) , cs_vaultManager_() , walletTxRecord_(new WalletTransactionRecord(cs_vaultManager_)) @@ -219,7 +222,6 @@ UnspentOutputs VaultManager::getManagedUTXOs(VaultUTXOFilters filter) const auto managedScriptsLimitsCopy = managedScripts_; for(const auto& hashAndTransaction: walletTxRecord_->mapWallet) { - uint256 hash = hashAndTransaction.first; const CWalletTx& tx = hashAndTransaction.second; if(!( (allInputsAreKnown(tx) && tx.IsCoinStake()) || tx.mapValue.count(VAULT_DEPOSIT_DESCRIPTION) > 0 )) continue; @@ -233,6 +235,7 @@ UnspentOutputs VaultManager::getManagedUTXOs(VaultUTXOFilters filter) const if( (filter & VaultUTXOFilters::MATURED) > 0 && blocksTillMaturity > 0 ) continue; if( (filter & VaultUTXOFilters::INMATURE) > 0 && blocksTillMaturity < 1 ) continue; + const uint256 hash = utxoHasher_.GetUtxoHash(tx); for(unsigned outputIndex = 0; outputIndex < tx.vout.size(); ++outputIndex) { const CTxOut& output = tx.vout[outputIndex]; diff --git a/divi/src/VaultManager.h b/divi/src/VaultManager.h index 273ebe053..d6ad0afbc 100644 --- a/divi/src/VaultManager.h +++ b/divi/src/VaultManager.h @@ -14,6 +14,7 @@ using UnspentOutputs = std::vector; class CTransaction; class CBlock; class CWalletTx; +class TransactionUtxoHasher; class uint256; class CTxOut; @@ -37,6 +38,7 @@ class VaultManager const I_MerkleTxConfirmationNumberCalculator& confirmationsCalculator_; I_VaultManagerDatabase& vaultManagerDB_; mutable CCriticalSection cs_vaultManager_; + const TransactionUtxoHasher& utxoHasher_; std::unique_ptr walletTxRecord_; std::unique_ptr outputTracker_; ManagedScripts managedScripts_; @@ -50,6 +52,7 @@ class VaultManager public: VaultManager( const I_MerkleTxConfirmationNumberCalculator& confirmationsCalculator, + const TransactionUtxoHasher& utxoHasher, I_VaultManagerDatabase& vaultManagerDB); ~VaultManager(); diff --git a/divi/src/test/VaultManager_tests.cpp b/divi/src/test/VaultManager_tests.cpp index 7099b16d3..71885a84b 100644 --- a/divi/src/test/VaultManager_tests.cpp +++ b/divi/src/test/VaultManager_tests.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -21,6 +22,7 @@ using ::testing::Invoke; struct VaultManagerTestFixture { public: + MockUtxoHasher utxoHasher; std::unique_ptr> mockPtr; std::unique_ptr fakeBlockIndexWithHashesResource; std::unique_ptr confirmationsCalculator; @@ -36,7 +38,7 @@ struct VaultManagerTestFixture *(fakeBlockIndexWithHashesResource->blockIndexByHash) )) , scriptGenerator() - , manager( new VaultManager(*confirmationsCalculator,*mockPtr )) + , manager( new VaultManager(*confirmationsCalculator, utxoHasher, *mockPtr)) { } ~VaultManagerTestFixture() @@ -190,7 +192,7 @@ BOOST_AUTO_TEST_CASE(willDiscountSpentUTXOs) manager->addTransaction(tx,&blockMiningFirstTx, true); CMutableTransaction otherTx; - otherTx.vin.emplace_back( COutPoint(tx.GetHash(), 1u) ); + otherTx.vin.emplace_back( COutPoint(utxoHasher.GetUtxoHash(tx), 1u) ); otherTx.vout.push_back(CTxOut(100,managedScript)); otherTx.vout.push_back(CTxOut(100,managedScript)); otherTx.vout.push_back(CTxOut(100,managedScript)); @@ -245,7 +247,7 @@ BOOST_AUTO_TEST_CASE(willCheckThatCoinstakeTransactionsAreDeepEnoughToSpend) manager->addTransaction(fundingTransaction,&blockMiningFundingTx, true); CMutableTransaction tx; - tx.vin.push_back(CTxIn(fundingTransaction.GetHash(),0u)); + tx.vin.push_back(CTxIn(utxoHasher.GetUtxoHash(fundingTransaction),0u)); CTxOut emptyFirstOutput; emptyFirstOutput.SetEmpty(); tx.vout.push_back(emptyFirstOutput); @@ -271,7 +273,7 @@ BOOST_AUTO_TEST_CASE(willLoadTransactionsFromDatabase) dummyTransaction.vout.push_back(CTxOut(100,scriptGenerator(10))); CMutableTransaction tx; - tx.vin.push_back(CTxIn(dummyTransaction.GetHash(),0u)); + tx.vin.push_back(CTxIn(utxoHasher.GetUtxoHash(dummyTransaction),0u)); tx.vout.push_back(CTxOut(100,managedScript)); tx.vout.push_back(CTxOut(100,managedScript)); tx.vout.push_back(CTxOut(100,managedScript)); @@ -309,7 +311,7 @@ BOOST_AUTO_TEST_CASE(willLoadTransactionsFromDatabase) return true; } )); - manager.reset(new VaultManager(*confirmationsCalculator, *mockPtr )); + manager.reset(new VaultManager(*confirmationsCalculator, utxoHasher, *mockPtr )); BOOST_CHECK_EQUAL(manager->getManagedUTXOs().size(), 4u); BOOST_CHECK(expectedTx==manager->getTransaction(tx.GetHash())); @@ -329,7 +331,7 @@ BOOST_AUTO_TEST_CASE(willLoadManyTransactionsFromDatabase) CMutableTransaction dummyTransaction; dummyTransaction.vout.push_back(CTxOut(100,scriptGenerator(10))); CMutableTransaction tx; - tx.vin.push_back(CTxIn(dummyTransaction.GetHash(),0u)); + tx.vin.push_back(CTxIn(utxoHasher.GetUtxoHash(dummyTransaction),0u)); tx.vout.push_back(CTxOut(randomSentAmount,scriptGenerator(10))); dummyTransactions.emplace_back(tx); @@ -352,7 +354,7 @@ BOOST_AUTO_TEST_CASE(willLoadManyTransactionsFromDatabase) return false; } )); - manager.reset(new VaultManager(*confirmationsCalculator, *mockPtr )); + manager.reset(new VaultManager(*confirmationsCalculator, utxoHasher, *mockPtr )); for(unsigned txCount =0 ; txCount < 10u; ++txCount) { @@ -698,7 +700,7 @@ BOOST_AUTO_TEST_CASE(willUpdatingDepositStatusWillPersist) manager->addTransaction(tx,&blockMiningFirstTx, true); BOOST_CHECK_EQUAL(manager->getTransaction(tx.GetHash()).mapValue.count("isVaultDeposit"),1u); - manager.reset( new VaultManager(*confirmationsCalculator,*mockPtr )); + manager.reset( new VaultManager(*confirmationsCalculator, utxoHasher, *mockPtr)); BOOST_CHECK_EQUAL(manager->getTransaction(tx.GetHash()).mapValue.count("isVaultDeposit"),1u); } @@ -966,4 +968,4 @@ BOOST_AUTO_TEST_CASE(willNotEraseManagedScriptToBackendOnRemovalMoreThanOnce) manager->removeManagedScript(managedScript); } -BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file +BOOST_AUTO_TEST_SUITE_END() diff --git a/divi/src/wallet.cpp b/divi/src/wallet.cpp index 45aa1abba..7569754c3 100644 --- a/divi/src/wallet.cpp +++ b/divi/src/wallet.cpp @@ -268,7 +268,7 @@ void CWallet::activateVaultMode() const std::string keystring = vchDefaultKey.GetID().ToString(); const std::string vaultID = std::string("vault_") + Hash160(keystring.begin(),keystring.end()).ToString().substr(0,10); vaultDB_.reset(new VaultManagerDatabase(vaultID,0)); - vaultManager_.reset(new VaultManager(*confirmationNumberCalculator_,*vaultDB_)); + vaultManager_.reset(new VaultManager(*confirmationNumberCalculator_, *utxoHasher, *vaultDB_)); for(const std::string& whitelistedVaultScript: settings.GetMultiParameter("-whitelisted_vault")) { auto byteVector = ParseHex(whitelistedVaultScript); From 68dee2e9550efbe24649f66406d5ba94c251bb4c Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Wed, 3 Nov 2021 15:45:26 +0100 Subject: [PATCH 08/14] Use outputhash in regtests. This updates the regtests to use "outputhash" for listunspent results in some places, to make sure the code will also work after activating segwit-light. Some other places remain where outputs are not from listunspent and that still needs to be updated when segwit-light gets activated generally, but this is a first step to reduce the amount of required changes then. --- divi/qa/rpc-tests/BadBlockTests.py | 2 +- divi/qa/rpc-tests/BlocksOnlyHaveSingleCoinstake.py | 2 +- divi/qa/rpc-tests/TxInputsStandardness.py | 2 +- divi/qa/rpc-tests/mnvaults.py | 13 +++++++------ divi/qa/rpc-tests/op_meta.py | 11 ++++++++--- divi/qa/rpc-tests/rawtransactions.py | 2 +- divi/qa/rpc-tests/smartfees.py | 14 ++++++++++++-- divi/qa/rpc-tests/txn_doublespend.py | 6 +++--- divi/qa/rpc-tests/util.py | 13 +------------ divi/qa/rpc-tests/vaultfork.py | 5 +++-- divi/qa/rpc-tests/wallet.py | 2 +- divi/qa/rpc-tests/wallet_sends.py | 4 ++-- 12 files changed, 41 insertions(+), 35 deletions(-) mode change 100644 => 100755 divi/qa/rpc-tests/BadBlockTests.py diff --git a/divi/qa/rpc-tests/BadBlockTests.py b/divi/qa/rpc-tests/BadBlockTests.py old mode 100644 new mode 100755 index 9d6f96455..3b0b08195 --- a/divi/qa/rpc-tests/BadBlockTests.py +++ b/divi/qa/rpc-tests/BadBlockTests.py @@ -49,7 +49,7 @@ def build_bad_sig_spend (self): amountToSend = int ((Decimal (inp["amount"]) - Decimal ('0.1')) * COIN) tx = CTransaction () tx.vout.append( CTxOut(amountToSend, scriptToSendTo ) ) - tx.vin.append (CTxIn (COutPoint (txid=inp["txid"], n=inp["vout"]))) + tx.vin.append (CTxIn (COutPoint (txid=inp["outputhash"], n=inp["vout"]))) unsigned = tx.serialize ().hex () diff --git a/divi/qa/rpc-tests/BlocksOnlyHaveSingleCoinstake.py b/divi/qa/rpc-tests/BlocksOnlyHaveSingleCoinstake.py index 190aa7a75..08a4b0563 100755 --- a/divi/qa/rpc-tests/BlocksOnlyHaveSingleCoinstake.py +++ b/divi/qa/rpc-tests/BlocksOnlyHaveSingleCoinstake.py @@ -42,7 +42,7 @@ def build_coinstake_tx (self): tx = CTransaction () tx.vout.append( CTxOut(0, CScript() ) ) tx.vout.append( CTxOut(amountToSend, scriptToSendTo ) ) - tx.vin.append (CTxIn (COutPoint (txid=inp["txid"], n=inp["vout"]))) + tx.vin.append (CTxIn (COutPoint (txid=inp["outputhash"], n=inp["vout"]))) unsigned = tx.serialize ().hex () diff --git a/divi/qa/rpc-tests/TxInputsStandardness.py b/divi/qa/rpc-tests/TxInputsStandardness.py index 79b7e6a3a..98e897764 100755 --- a/divi/qa/rpc-tests/TxInputsStandardness.py +++ b/divi/qa/rpc-tests/TxInputsStandardness.py @@ -28,7 +28,7 @@ def generateOutput (self, addr, amount): tx = self.node.getrawtransaction (txid, 1) for i in range (len (tx["vout"])): if tx["vout"][i]["scriptPubKey"]["addresses"] == [addr]: - return (txid, i) + return (tx["txid"], i) raise AssertionError ("failed to find destination address") diff --git a/divi/qa/rpc-tests/mnvaults.py b/divi/qa/rpc-tests/mnvaults.py index 330736cd6..f96a2d720 100755 --- a/divi/qa/rpc-tests/mnvaults.py +++ b/divi/qa/rpc-tests/mnvaults.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2020 The DIVI developers +# Copyright (c) 2020-2021 The DIVI developers # Distributed under the MIT/X11 software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -26,7 +26,7 @@ class MnVaultsTest (MnTestFramework): def __init__ (self): - super (MnVaultsTest, self).__init__ () + super ().__init__ () self.base_args = ["-debug=masternode", "-debug=mocktime"] self.cfg = None self.number_of_nodes=7 @@ -94,6 +94,7 @@ def fund_vault (self): amount = 100 txid = self.nodes[0].sendtoaddress (addr, amount) raw = self.nodes[0].getrawtransaction (txid, 1) + outputId = raw["txid"] vout = None for i in range (len (raw["vout"])): o = raw["vout"][i] @@ -112,19 +113,19 @@ def fund_vault (self): data = self.nodes[0].validateaddress (unvaultAddr) tx = CTransaction () - tx.vin.append (CTxIn (COutPoint (txid=txid, n=vout))) + tx.vin.append (CTxIn (COutPoint (txid=outputId, n=vout))) tx.vout.append (CTxOut (amount * COIN, unhexlify (data["scriptPubKey"]))) unsigned = ToHex (tx) validated = self.nodes[0].validateaddress (addr) script = validated["scriptPubKey"] - prevtx = [{"txid": txid, "vout": vout, "scriptPubKey": script}] + prevtx = [{"txid": outputId, "vout": vout, "scriptPubKey": script}] signed = self.nodes[0].signrawtransaction (unsigned, prevtx, [privkey], "SINGLE|ANYONECANPAY") assert_equal (signed["complete"], True) self.unvaultTx = signed["hex"] - self.setup_masternode(2,1,"mn","copper",{"txhash":txid,"vout":vout}) + self.setup_masternode(2,1,"mn","copper",{"txhash":outputId,"vout":vout}) self.cfg = self.setup[1].cfg # FIXME: Use reward address from node 0. self.cfg.rewardAddr = addr @@ -231,7 +232,7 @@ def unvault (self): data = self.nodes[0].validateaddress (changeAddr) tx = FromHex (CTransaction (), self.unvaultTx) - tx.vin.append (CTxIn (COutPoint (txid=inp["txid"], n=inp["vout"]))) + tx.vin.append (CTxIn (COutPoint (txid=inp["outputhash"], n=inp["vout"]))) tx.vout.append (CTxOut (change, unhexlify (data["scriptPubKey"]))) partial = ToHex (tx) diff --git a/divi/qa/rpc-tests/op_meta.py b/divi/qa/rpc-tests/op_meta.py index 87b016cf7..ae90f6f02 100755 --- a/divi/qa/rpc-tests/op_meta.py +++ b/divi/qa/rpc-tests/op_meta.py @@ -46,7 +46,11 @@ def build_op_meta_tx (self, utxos, payload, fee): inp = None for i in range (len (utxos)): if utxos[i]["amount"] >= required: - inp = utxos[i] + inp = { + "txid": utxos[i]["outputhash"], + "vout": utxos[i]["vout"], + "amount": utxos[i]["amount"], + } del utxos[i] break assert inp is not None, "found no suitable output" @@ -59,8 +63,9 @@ def build_op_meta_tx (self, utxos, payload, fee): tx = self.node.createrawtransaction ([inp], {changeAddr: change}) signed = self.node.signrawtransaction (tx) assert_equal (signed["complete"], True) - txid = self.node.sendrawtransaction (signed["hex"]) - inp["txid"] = txid + data = self.node.decoderawtransaction (signed["hex"]) + self.node.sendrawtransaction (signed["hex"]) + inp["txid"] = data["txid"] inp["vout"] = 0 inp["amount"] = change diff --git a/divi/qa/rpc-tests/rawtransactions.py b/divi/qa/rpc-tests/rawtransactions.py index 1696f51ba..6a83c084a 100755 --- a/divi/qa/rpc-tests/rawtransactions.py +++ b/divi/qa/rpc-tests/rawtransactions.py @@ -26,7 +26,7 @@ def find_output (self, node, value): for u in node.listunspent (): if u["amount"] == value: - return {"txid": u["txid"], "vout": u["vout"]} + return {"txid": u["outputhash"], "vout": u["vout"]} raise AssertionError ("no output with value %s found" % str (value)) diff --git a/divi/qa/rpc-tests/smartfees.py b/divi/qa/rpc-tests/smartfees.py index 1c7c3b218..700577c8f 100755 --- a/divi/qa/rpc-tests/smartfees.py +++ b/divi/qa/rpc-tests/smartfees.py @@ -12,6 +12,17 @@ from util import * +def find_output(node, txid, amount): + """ + Return index to output of txid with value amount + Raises exception if there is none. + """ + txdata = node.getrawtransaction(txid, 1) + for i in range(len(txdata["vout"])): + if txdata["vout"][i]["value"] == amount: + return {"txid": txdata["txid"], "vout": i} + raise RuntimeError("find_output txid %s : %s not found"%(txid,str(amount))) + def send_zeropri_transaction(from_node, to_node, amount, fee): """ Create&broadcast a zero-priority transaction. @@ -31,10 +42,9 @@ def send_zeropri_transaction(from_node, to_node, amount, fee): self_signresult = from_node.signrawtransaction(self_rawtx) self_txid = from_node.sendrawtransaction(self_signresult["hex"], True) - vout = find_output(from_node, self_txid, amount+fee) # Now immediately spend the output to create a 1-input, 1-output # zero-priority transaction: - inputs = [ { "txid" : self_txid, "vout" : vout } ] + inputs = [find_output(from_node, self_txid, amount + fee)] outputs = { to_node.getnewaddress() : float(amount) } rawtx = from_node.createrawtransaction(inputs, outputs) diff --git a/divi/qa/rpc-tests/txn_doublespend.py b/divi/qa/rpc-tests/txn_doublespend.py index 6ba135c88..e7eae7ce5 100755 --- a/divi/qa/rpc-tests/txn_doublespend.py +++ b/divi/qa/rpc-tests/txn_doublespend.py @@ -51,7 +51,7 @@ def create_doublespend_for_later(self,sender, node1_address): utxos_by_account = self.collect_utxos_by_account(sender,["foo","bar"]) inputs = [] for utxo in utxos_by_account["foo"]: - inputs.append({ "txid" : utxo["txid"], "vout" : utxo["vout"]} ) + inputs.append({ "txid" : utxo["outputhash"], "vout" : utxo["vout"]} ) outputs = {} outputs[self.foo_address] = 9.999 @@ -65,9 +65,9 @@ def create_daisy_chain_transactions(self,sender,node1_address): utxos_by_account = self.collect_utxos_by_account(sender,["foo","bar"]) inputs = [] for utxo in utxos_by_account["foo"]: - inputs.append({ "txid" : utxo["txid"], "vout" : utxo["vout"], "address" : utxo["address"] } ) + inputs.append({ "txid" : utxo["outputhash"], "vout" : utxo["vout"], "address" : utxo["address"] } ) for utxo in utxos_by_account["bar"]: - inputs.append({ "txid" : utxo["txid"], "vout" : utxo["vout"], "address" : utxo["address"] } ) + inputs.append({ "txid" : utxo["outputhash"], "vout" : utxo["vout"], "address" : utxo["address"] } ) outputs = {} outputs[self.bar_address] = 29.999 diff --git a/divi/qa/rpc-tests/util.py b/divi/qa/rpc-tests/util.py index 5d4137196..e261de37f 100644 --- a/divi/qa/rpc-tests/util.py +++ b/divi/qa/rpc-tests/util.py @@ -216,17 +216,6 @@ def connect_nodes_bi(nodes, a, b): connect_nodes(nodes[a], b) connect_nodes(nodes[b], a) -def find_output(node, txid, amount): - """ - Return index to output of txid with value amount - Raises exception if there is none. - """ - txdata = node.getrawtransaction(txid, 1) - for i in range(len(txdata["vout"])): - if txdata["vout"][i]["value"] == amount: - return i - raise RuntimeError("find_output txid %s : %s not found"%(txid,str(amount))) - def gather_inputs(from_node, amount_needed, confirmations_required=1): """ Return a random set of unspent txouts that are enough to pay amount_needed @@ -239,7 +228,7 @@ def gather_inputs(from_node, amount_needed, confirmations_required=1): while total_in < amount_needed and len(utxo) > 0: t = utxo.pop() total_in += t["amount"] - inputs.append({ "txid" : t["txid"], "vout" : t["vout"], "address" : t["address"] } ) + inputs.append({ "txid" : t["outputhash"], "vout" : t["vout"], "address" : t["address"] } ) if total_in < amount_needed: raise RuntimeError("Insufficient funds: need %d, have %d"%(amount_needed, total_in)) return (total_in, inputs) diff --git a/divi/qa/rpc-tests/vaultfork.py b/divi/qa/rpc-tests/vaultfork.py index 662820cc2..f4789af4e 100755 --- a/divi/qa/rpc-tests/vaultfork.py +++ b/divi/qa/rpc-tests/vaultfork.py @@ -31,10 +31,11 @@ def fund_vault (self, owner, staker, amount): """ txid = owner.fundvault (staker.getnewaddress (), amount)["txhash"] - outputs = owner.getrawtransaction (txid, 1)["vout"] + data = owner.getrawtransaction (txid, 1) + outputs = data["vout"] for n in range (len (outputs)): if outputs[n]["scriptPubKey"]["type"] == "vault": - return {"txid": txid, "vout": n} + return {"txid": data["txid"], "vout": n} raise AssertionError ("constructed transaction has no vault output") diff --git a/divi/qa/rpc-tests/wallet.py b/divi/qa/rpc-tests/wallet.py index 06038a16b..2beaeeb6a 100755 --- a/divi/qa/rpc-tests/wallet.py +++ b/divi/qa/rpc-tests/wallet.py @@ -70,7 +70,7 @@ def run_test (self): for utxo in node0utxos: inputs = [] outputs = {} - inputs.append({ "txid" : utxo["txid"], "vout" : utxo["vout"]}) + inputs.append({ "txid" : utxo["outputhash"], "vout" : utxo["vout"]}) outputs[self.nodes[2].getnewaddress("from1")] = utxo["amount"] raw_tx = self.nodes[0].createrawtransaction(inputs, outputs) txns_to_send.append(self.nodes[0].signrawtransaction(raw_tx)) diff --git a/divi/qa/rpc-tests/wallet_sends.py b/divi/qa/rpc-tests/wallet_sends.py index 62405ea98..ed1790fd9 100755 --- a/divi/qa/rpc-tests/wallet_sends.py +++ b/divi/qa/rpc-tests/wallet_sends.py @@ -92,7 +92,7 @@ def ensure_complex_spends_resolve_balances_correctly(self,accountName=""): sender_utxo = sender.listunspent()[0] tx = CTransaction () - tx.vin.append (CTxIn (COutPoint (txid=sender_utxo["txid"], n=sender_utxo["vout"]))) + tx.vin.append (CTxIn (COutPoint (txid=sender_utxo["outputhash"], n=sender_utxo["vout"]))) tx.vin.append (CTxIn (COutPoint (txid=receiver_utxo[0], n=receiver_utxo[1]))) tx.vin.append (CTxIn (COutPoint (txid=multisigTxID, n=multisigOutputIndex))) @@ -154,7 +154,7 @@ def join_sends_compute_balance_correctly(self): sender_utxo = sender.listunspent()[0] tx = CTransaction () - tx.vin.append (CTxIn (COutPoint (txid=sender_utxo["txid"], n=sender_utxo["vout"]))) + tx.vin.append (CTxIn (COutPoint (txid=sender_utxo["outputhash"], n=sender_utxo["vout"]))) tx.vin.append (CTxIn (COutPoint (txid=receiver_utxo[0], n=receiver_utxo[1]))) amountToSend = int ((Decimal (2500.0) - Decimal ('0.1')) * COIN) From 62447d087b31d7c8fe618ff7756dceb0fdaf3d47 Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Tue, 15 Jun 2021 16:17:50 +0200 Subject: [PATCH 09/14] Type-safe OutputHash. This introduces a new, different type OutputHash for hashes of outputs, e.g. in COutPoint and all related places in the code. This type is functionally equivalent to uint256, but it enables the compiler to ensure that all places that should be using an UTXO hasher are actually using it. --- .../BlockMemoryPoolTransactionCollector.cpp | 5 +- .../src/BlockMemoryPoolTransactionCollector.h | 5 +- divi/src/I_StakingCoinSelector.h | 3 +- divi/src/IndexDatabaseUpdates.h | 3 +- divi/src/Logging.cpp | 2 + divi/src/Logging.h | 1 + divi/src/Makefile.am | 1 + divi/src/MasternodeModule.cpp | 2 +- divi/src/OrphanTransactions.cpp | 4 +- divi/src/OrphanTransactions.h | 3 +- divi/src/OutputHash.h | 80 +++++++++++++++++++ divi/src/SpentOutputTracker.cpp | 2 +- divi/src/SpentOutputTracker.h | 4 +- divi/src/TransactionDiskAccessor.cpp | 7 +- divi/src/TransactionDiskAccessor.h | 4 +- divi/src/UtxoCheckingAndUpdating.cpp | 4 +- divi/src/UtxoCheckingAndUpdating.h | 6 +- divi/src/VaultManager.cpp | 2 +- divi/src/WalletTransactionRecord.cpp | 5 ++ divi/src/WalletTransactionRecord.h | 1 + divi/src/addressindex.h | 8 +- divi/src/bloom.cpp | 4 +- divi/src/coincontrol.h | 2 +- divi/src/coins.cpp | 14 ++-- divi/src/coins.h | 27 ++++--- divi/src/divi-tx.cpp | 4 +- divi/src/init.cpp | 2 +- divi/src/main.cpp | 2 +- divi/src/masternode-payments.cpp | 6 +- divi/src/masternode.cpp | 2 +- divi/src/masternodeconfig.cpp | 3 +- divi/src/primitives/transaction.cpp | 4 +- divi/src/primitives/transaction.h | 7 +- divi/src/rpcblockchain.cpp | 4 +- divi/src/rpcmisc.cpp | 4 +- divi/src/rpcrawtransaction.cpp | 14 ++-- divi/src/rpcwallet.cpp | 2 +- divi/src/spentindex.h | 4 +- divi/src/txdb.cpp | 7 +- divi/src/txdb.h | 4 +- divi/src/txmempool.cpp | 18 ++--- divi/src/txmempool.h | 10 +-- divi/src/wallet.cpp | 20 +++-- divi/src/wallet.h | 5 +- 44 files changed, 218 insertions(+), 103 deletions(-) create mode 100644 divi/src/OutputHash.h diff --git a/divi/src/BlockMemoryPoolTransactionCollector.cpp b/divi/src/BlockMemoryPoolTransactionCollector.cpp index dd70a41b8..f4a6d737e 100644 --- a/divi/src/BlockMemoryPoolTransactionCollector.cpp +++ b/divi/src/BlockMemoryPoolTransactionCollector.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -52,7 +53,7 @@ class COrphan { public: const CTransaction* ptx; - std::set setDependsOn; + std::set setDependsOn; CFeeRate feeRate; double coinAgeOfInputsPerByte; @@ -144,7 +145,7 @@ void BlockMemoryPoolTransactionCollector::ComputeTransactionPriority( void BlockMemoryPoolTransactionCollector::AddDependingTransactionsToPriorityQueue( DependingTransactionsMap& dependentTransactions, - const uint256& hash, + const OutputHash& hash, std::vector& vecPriority, TxPriorityCompare& comparer) const { diff --git a/divi/src/BlockMemoryPoolTransactionCollector.h b/divi/src/BlockMemoryPoolTransactionCollector.h index 7f4aea97c..0b5acbd7d 100644 --- a/divi/src/BlockMemoryPoolTransactionCollector.h +++ b/divi/src/BlockMemoryPoolTransactionCollector.h @@ -28,6 +28,7 @@ class CTxMemPool; class CBlockTemplate; class CBlockHeader; class CFeeRate; +class OutputHash; class Settings; struct PrioritizedTransactionData @@ -52,7 +53,7 @@ class CChain; class BlockMemoryPoolTransactionCollector: public I_BlockTransactionCollector { private: - using DependingTransactionsMap = std::map>>; + using DependingTransactionsMap = std::map>>; CCoinsViewCache* baseCoinsViewCache_; const CChain& activeChain_; @@ -78,7 +79,7 @@ class BlockMemoryPoolTransactionCollector: public I_BlockTransactionCollector std::vector& vecPriority) const; void AddDependingTransactionsToPriorityQueue( DependingTransactionsMap& mapDependers, - const uint256& hash, + const OutputHash& hash, std::vector& vecPriority, TxPriorityCompare& comparer) const; diff --git a/divi/src/I_StakingCoinSelector.h b/divi/src/I_StakingCoinSelector.h index d8f427c19..2a6fd76d5 100644 --- a/divi/src/I_StakingCoinSelector.h +++ b/divi/src/I_StakingCoinSelector.h @@ -6,6 +6,7 @@ #include class CMerkleTx; +class OutputHash; class I_StakingCoinSelector { @@ -22,6 +23,6 @@ class I_StakingWallet: public virtual CKeyStore, public I_StakingCoinSelector, p /** Returns the UTXO hash that should be used for spending outputs * from the given transaction (which should be part of the wallet). */ - virtual uint256 GetUtxoHash(const CMerkleTx& tx) const = 0; + virtual OutputHash GetUtxoHash(const CMerkleTx& tx) const = 0; }; #endif// I_STAKING_COIN_SELECTOR_H diff --git a/divi/src/IndexDatabaseUpdates.h b/divi/src/IndexDatabaseUpdates.h index 9abc9857e..f3d762cb3 100644 --- a/divi/src/IndexDatabaseUpdates.h +++ b/divi/src/IndexDatabaseUpdates.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -35,7 +36,7 @@ struct IndexDatabaseUpdates struct TransactionLocationReference { uint256 hash; - uint256 utxoHash; + OutputHash utxoHash; unsigned blockHeight; int transactionIndex; diff --git a/divi/src/Logging.cpp b/divi/src/Logging.cpp index d114f356f..7a01674ae 100644 --- a/divi/src/Logging.cpp +++ b/divi/src/Logging.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -22,6 +23,7 @@ bool fLogIPs = false; extern Settings& settings; +LOG_FORMAT_WITH_TOSTRING(OutputHash) LOG_FORMAT_WITH_TOSTRING(uint256) namespace diff --git a/divi/src/Logging.h b/divi/src/Logging.h index c63fe0d00..e58eca364 100644 --- a/divi/src/Logging.h +++ b/divi/src/Logging.h @@ -50,6 +50,7 @@ template } \ }; LOG_WITH_CONVERSION(CLockLocation) +LOG_WITH_CONVERSION(OutputHash) LOG_WITH_CONVERSION(uint256) /* Defined in Logging-common.cpp. */ diff --git a/divi/src/Makefile.am b/divi/src/Makefile.am index 634845136..1f208bf05 100644 --- a/divi/src/Makefile.am +++ b/divi/src/Makefile.am @@ -258,6 +258,7 @@ BITCOIN_CORE_H = \ BlockMemoryPoolTransactionCollector.h \ CoinMinter.h \ CoinMintingModule.h \ + OutputHash.h \ PoSTransactionCreator.h \ PeerNotificationOfMintService.h \ MonthlyWalletBackupCreator.h \ diff --git a/divi/src/MasternodeModule.cpp b/divi/src/MasternodeModule.cpp index 9d4a4a33d..a9712ed53 100644 --- a/divi/src/MasternodeModule.cpp +++ b/divi/src/MasternodeModule.cpp @@ -434,7 +434,7 @@ void LockUpMasternodeCollateral(const Settings& settings, std::function(mne.getOutputIndex())); + COutPoint outpoint(OutputHash(mnTxHash), boost::lexical_cast(mne.getOutputIndex())); walletUtxoLockingFunction(outpoint); } } diff --git a/divi/src/OrphanTransactions.cpp b/divi/src/OrphanTransactions.cpp index b117f7099..f1142b136 100644 --- a/divi/src/OrphanTransactions.cpp +++ b/divi/src/OrphanTransactions.cpp @@ -17,14 +17,14 @@ struct COrphanTx { NodeId fromPeer; }; std::map mapOrphanTransactions; -std::map > mapOrphanTransactionsByPrev; +std::map > mapOrphanTransactionsByPrev; ////////////////////////////////////////////////////////////////////////////// // // mapOrphanTransactions // -const std::set& GetOrphanSpendingTransactionIds(const uint256& txHash) +const std::set& GetOrphanSpendingTransactionIds(const OutputHash& txHash) { static std::set emptySet; const auto it = mapOrphanTransactionsByPrev.find(txHash); diff --git a/divi/src/OrphanTransactions.h b/divi/src/OrphanTransactions.h index 22b7f7790..663fedef9 100644 --- a/divi/src/OrphanTransactions.h +++ b/divi/src/OrphanTransactions.h @@ -1,10 +1,11 @@ #ifndef ORPHAN_TRANSACTIONS_H #define ORPHAN_TRANSACTIONS_H #include +#include #include #include class CTransaction; -const std::set& GetOrphanSpendingTransactionIds(const uint256& txHash); +const std::set& GetOrphanSpendingTransactionIds(const OutputHash& txHash); const CTransaction& GetOrphanTransaction(const uint256& txHash, NodeId& peer); bool OrphanTransactionIsKnown(const uint256& hash); bool AddOrphanTx(const CTransaction& tx, NodeId peer); diff --git a/divi/src/OutputHash.h b/divi/src/OutputHash.h new file mode 100644 index 000000000..3fd6a1905 --- /dev/null +++ b/divi/src/OutputHash.h @@ -0,0 +1,80 @@ +#ifndef OUTPUT_HASH_H +#define OUTPUT_HASH_H + +#include "serialize.h" +#include "uint256.h" + +/** This class is "equivalent" to a uint256, but semantically it represents + * a specific use, namely the hash used for an outpoint in the UTXO set + * and referred to by following transactions. This is the normal txid + * originally, but may be changed in the future e.g. with segwit-light. */ +class OutputHash +{ + +private: + + /** The actual hash value. */ + uint256 value; + +public: + + OutputHash () = default; + OutputHash (const OutputHash&) = default; + OutputHash& operator= (const OutputHash&) = default; + + explicit OutputHash (const uint256& val) + : value(val) + {} + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp (Stream& s, Operation ser_action, + int nType, int nVersion) + { + READWRITE (value); + } + + inline const uint256& GetValue () const + { + return value; + } + + inline void SetNull () + { + value.SetNull (); + } + + inline bool IsNull () const + { + return value.IsNull (); + } + + inline std::string ToString () const + { + return value.ToString (); + } + + inline std::string GetHex () const + { + return value.GetHex (); + } + + inline friend bool operator== (const OutputHash& a, const OutputHash& b) + { + return a.value == b.value; + } + + inline friend bool operator!= (const OutputHash& a, const OutputHash& b) + { + return !(a == b); + } + + inline friend bool operator< (const OutputHash& a, const OutputHash& b) + { + return a.value < b.value; + } + +}; + +#endif // OUTPUT_HASH_H diff --git a/divi/src/SpentOutputTracker.cpp b/divi/src/SpentOutputTracker.cpp index 0844b447e..bda598ec0 100644 --- a/divi/src/SpentOutputTracker.cpp +++ b/divi/src/SpentOutputTracker.cpp @@ -19,7 +19,7 @@ SpentOutputTracker::SpentOutputTracker( * Outpoint is spent if any non-conflicted transaction * spends it: */ -bool SpentOutputTracker::IsSpent(const uint256& hash, unsigned int n) const +bool SpentOutputTracker::IsSpent(const OutputHash& hash, unsigned int n) const { const COutPoint outpoint(hash, n); std::pair range; diff --git a/divi/src/SpentOutputTracker.h b/divi/src/SpentOutputTracker.h index 673110514..2dcc85c89 100644 --- a/divi/src/SpentOutputTracker.h +++ b/divi/src/SpentOutputTracker.h @@ -7,7 +7,7 @@ class WalletTransactionRecord; class COutPoint; -class uint256; +class OutputHash; class CWalletTx; class I_MerkleTxConfirmationNumberCalculator; @@ -35,7 +35,7 @@ class SpentOutputTracker std::pair UpdateSpends( const CWalletTx& newlyAddedTransaction, bool loadedFromDisk=false); - bool IsSpent(const uint256& hash, unsigned int n) const; + bool IsSpent(const OutputHash& hash, unsigned int n) const; std::set GetConflictingTxHashes(const CWalletTx& tx) const; }; #endif// SPENT_OUTPUT_TRACKER_H diff --git a/divi/src/TransactionDiskAccessor.cpp b/divi/src/TransactionDiskAccessor.cpp index 20fd725a4..68308f513 100644 --- a/divi/src/TransactionDiskAccessor.cpp +++ b/divi/src/TransactionDiskAccessor.cpp @@ -58,7 +58,7 @@ bool GetTransaction(const uint256& hash, CTransaction& txOut, uint256& hashBlock int nHeight = -1; { CCoinsViewCache& view = *pcoinsTip; - const CCoins* coins = view.AccessCoins(hash); + const CCoins* coins = view.AccessCoins(OutputHash(hash)); if (coins) nHeight = coins->nHeight; } @@ -83,6 +83,11 @@ bool GetTransaction(const uint256& hash, CTransaction& txOut, uint256& hashBlock return false; } +bool GetTransaction(const OutputHash& hash, CTransaction& txOut, uint256& hashBlock, bool fAllowSlow) +{ + return GetTransaction(hash.GetValue(), txOut, hashBlock, fAllowSlow); +} + bool CollateralIsExpectedAmount(const COutPoint &outpoint, int64_t expectedAmount) { CCoins coins; diff --git a/divi/src/TransactionDiskAccessor.h b/divi/src/TransactionDiskAccessor.h index e9b2ac61f..a1a414d34 100644 --- a/divi/src/TransactionDiskAccessor.h +++ b/divi/src/TransactionDiskAccessor.h @@ -4,8 +4,10 @@ class uint256; class CTransaction; class COutPoint; +class OutputHash; /** Get transaction from mempool or disk **/ bool GetTransaction(const uint256& hash, CTransaction& tx, uint256& hashBlock, bool fAllowSlow); +bool GetTransaction(const OutputHash& hash, CTransaction& tx, uint256& hashBlock, bool fAllowSlow); bool CollateralIsExpectedAmount(const COutPoint &outpoint, int64_t expectedAmount); -#endif // TRANSACTION_DISK_ACCESSOR_H \ No newline at end of file +#endif // TRANSACTION_DISK_ACCESSOR_H diff --git a/divi/src/UtxoCheckingAndUpdating.cpp b/divi/src/UtxoCheckingAndUpdating.cpp index 5d7abb1da..031fdd3dc 100644 --- a/divi/src/UtxoCheckingAndUpdating.cpp +++ b/divi/src/UtxoCheckingAndUpdating.cpp @@ -12,9 +12,9 @@ #include #include -uint256 BlockUtxoHasher::GetUtxoHash(const CTransaction& tx) const +OutputHash BlockUtxoHasher::GetUtxoHash(const CTransaction& tx) const { - return tx.GetHash(); + return OutputHash(tx.GetHash()); } void UpdateCoinsWithTransaction(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo& txundo, diff --git a/divi/src/UtxoCheckingAndUpdating.h b/divi/src/UtxoCheckingAndUpdating.h index da4d76ed5..5230070b5 100644 --- a/divi/src/UtxoCheckingAndUpdating.h +++ b/divi/src/UtxoCheckingAndUpdating.h @@ -1,5 +1,7 @@ #ifndef UTXO_CHECKING_AND_UPDATING_H #define UTXO_CHECKING_AND_UPDATING_H + +#include #include #include #include @@ -38,7 +40,7 @@ class TransactionUtxoHasher TransactionUtxoHasher(const TransactionUtxoHasher&) = delete; void operator=(const TransactionUtxoHasher&) = delete; - virtual uint256 GetUtxoHash(const CTransaction& tx) const = 0; + virtual OutputHash GetUtxoHash(const CTransaction& tx) const = 0; }; @@ -56,7 +58,7 @@ class BlockUtxoHasher : public TransactionUtxoHasher public: - uint256 GetUtxoHash(const CTransaction& tx) const override; + OutputHash GetUtxoHash(const CTransaction& tx) const override; }; diff --git a/divi/src/VaultManager.cpp b/divi/src/VaultManager.cpp index f96dd557a..302ec1d54 100644 --- a/divi/src/VaultManager.cpp +++ b/divi/src/VaultManager.cpp @@ -235,7 +235,7 @@ UnspentOutputs VaultManager::getManagedUTXOs(VaultUTXOFilters filter) const if( (filter & VaultUTXOFilters::MATURED) > 0 && blocksTillMaturity > 0 ) continue; if( (filter & VaultUTXOFilters::INMATURE) > 0 && blocksTillMaturity < 1 ) continue; - const uint256 hash = utxoHasher_.GetUtxoHash(tx); + const OutputHash hash(utxoHasher_.GetUtxoHash(tx)); for(unsigned outputIndex = 0; outputIndex < tx.vout.size(); ++outputIndex) { const CTxOut& output = tx.vout[outputIndex]; diff --git a/divi/src/WalletTransactionRecord.cpp b/divi/src/WalletTransactionRecord.cpp index ad8739d6c..df32ef5b0 100644 --- a/divi/src/WalletTransactionRecord.cpp +++ b/divi/src/WalletTransactionRecord.cpp @@ -41,6 +41,11 @@ const CWalletTx* WalletTransactionRecord::GetWalletTx(const uint256& hash) const return nullptr; } +const CWalletTx* WalletTransactionRecord::GetWalletTx(const OutputHash& hash) const +{ + return GetWalletTx(hash.GetValue()); +} + std::vector WalletTransactionRecord::GetWalletTransactionReferences() const { AssertLockHeld(cs_walletTxRecord); diff --git a/divi/src/WalletTransactionRecord.h b/divi/src/WalletTransactionRecord.h index 91237dd73..54d2b977f 100644 --- a/divi/src/WalletTransactionRecord.h +++ b/divi/src/WalletTransactionRecord.h @@ -21,6 +21,7 @@ struct WalletTransactionRecord WalletTransactionRecord(CCriticalSection& requiredWalletLock,const std::string& walletFilename); WalletTransactionRecord(CCriticalSection& requiredWalletLock); const CWalletTx* GetWalletTx(const uint256& hash) const; + const CWalletTx* GetWalletTx(const OutputHash& hash) const; /** Tries to look up a transaction in the wallet, either by hash (txid) or * the bare txid that is used after segwit-light to identify outputs. */ diff --git a/divi/src/addressindex.h b/divi/src/addressindex.h index 0cd6c5694..3ab07dab8 100644 --- a/divi/src/addressindex.h +++ b/divi/src/addressindex.h @@ -15,10 +15,10 @@ struct CMempoolAddressDelta { int64_t time; CAmount amount; - uint256 prevhash; + OutputHash prevhash; unsigned int prevout; - CMempoolAddressDelta(int64_t t, CAmount a, const uint256& hash, unsigned int out) { + CMempoolAddressDelta(int64_t t, CAmount a, const OutputHash& hash, unsigned int out) { time = t; amount = a; prevhash = hash; @@ -219,7 +219,7 @@ struct CAddressIndexIteratorHeightKey { struct CAddressUnspentKey { unsigned int type; uint160 hashBytes; - uint256 txhash; + OutputHash txhash; size_t index; size_t GetSerializeSize(int nType, int nVersion) const { @@ -240,7 +240,7 @@ struct CAddressUnspentKey { index = ser_readdata32(s); } - CAddressUnspentKey(unsigned int addressType, uint160 addressHash, const uint256& txid, size_t indexValue) { + CAddressUnspentKey(unsigned int addressType, uint160 addressHash, const OutputHash& txid, size_t indexValue) { type = addressType; hashBytes = addressHash; txhash = txid; diff --git a/divi/src/bloom.cpp b/divi/src/bloom.cpp index 72a245838..bf878858c 100644 --- a/divi/src/bloom.cpp +++ b/divi/src/bloom.cpp @@ -141,13 +141,13 @@ bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx) if (data.size() != 0 && contains(data)) { fFound = true; if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_ALL) - insert(COutPoint(hash, i)); + insert(COutPoint(OutputHash(hash), i)); else if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_P2PUBKEY_ONLY) { txnouttype type; std::vector > vSolutions; if (ExtractScriptPubKeyFormat(txout.scriptPubKey, type, vSolutions) && (type == TX_PUBKEY || type == TX_MULTISIG)) - insert(COutPoint(hash, i)); + insert(COutPoint(OutputHash(hash), i)); } break; } diff --git a/divi/src/coincontrol.h b/divi/src/coincontrol.h index 181b5ad55..13602f631 100644 --- a/divi/src/coincontrol.h +++ b/divi/src/coincontrol.h @@ -49,7 +49,7 @@ class CCoinControl return (setSelected.size() > 0); } - bool IsSelected(const uint256& hash, unsigned int n) const + bool IsSelected(const OutputHash& hash, unsigned int n) const { COutPoint outpt(hash, n); return (setSelected.count(outpt) > 0); diff --git a/divi/src/coins.cpp b/divi/src/coins.cpp index 63e829824..5688acd76 100644 --- a/divi/src/coins.cpp +++ b/divi/src/coins.cpp @@ -153,8 +153,8 @@ std::string CCoins::ToString() const CCoinsViewBacked::CCoinsViewBacked() : base(nullptr) {} CCoinsViewBacked::CCoinsViewBacked(CCoinsView* viewIn) : base(viewIn) {} -bool CCoinsViewBacked::GetCoins(const uint256& txid, CCoins& coins) const { return base? base->GetCoins(txid, coins):false; } -bool CCoinsViewBacked::HaveCoins(const uint256& txid) const { return base? base->HaveCoins(txid):false; } +bool CCoinsViewBacked::GetCoins(const OutputHash& txid, CCoins& coins) const { return base? base->GetCoins(txid, coins):false; } +bool CCoinsViewBacked::HaveCoins(const OutputHash& txid) const { return base? base->HaveCoins(txid):false; } uint256 CCoinsViewBacked::GetBestBlock() const { return base? base->GetBestBlock():uint256(0); } void CCoinsViewBacked::SetBackend(CCoinsView& viewIn) { base = &viewIn; } void CCoinsViewBacked::DettachBackend() { base = nullptr; } @@ -171,7 +171,7 @@ CCoinsViewCache::~CCoinsViewCache() assert(!hasModifier); } -CCoinsMap::const_iterator CCoinsViewCache::FetchCoins(const uint256& txid) const +CCoinsMap::const_iterator CCoinsViewCache::FetchCoins(const OutputHash& txid) const { CCoinsMap::iterator it = cacheCoins.find(txid); if (it != cacheCoins.end()) @@ -189,7 +189,7 @@ CCoinsMap::const_iterator CCoinsViewCache::FetchCoins(const uint256& txid) const return ret; } -bool CCoinsViewCache::GetCoins(const uint256& txid, CCoins& coins) const +bool CCoinsViewCache::GetCoins(const OutputHash& txid, CCoins& coins) const { CCoinsMap::const_iterator it = FetchCoins(txid); if (it != cacheCoins.end()) { @@ -199,7 +199,7 @@ bool CCoinsViewCache::GetCoins(const uint256& txid, CCoins& coins) const return false; } -CCoinsModifier CCoinsViewCache::ModifyCoins(const uint256& txid) +CCoinsModifier CCoinsViewCache::ModifyCoins(const OutputHash& txid) { assert(!hasModifier); std::pair ret = cacheCoins.insert(std::make_pair(txid, CCoinsCacheEntry())); @@ -218,7 +218,7 @@ CCoinsModifier CCoinsViewCache::ModifyCoins(const uint256& txid) return CCoinsModifier(*this, ret.first); } -const CCoins* CCoinsViewCache::AccessCoins(const uint256& txid) const +const CCoins* CCoinsViewCache::AccessCoins(const OutputHash& txid) const { CCoinsMap::const_iterator it = FetchCoins(txid); if (it == cacheCoins.end()) { @@ -228,7 +228,7 @@ const CCoins* CCoinsViewCache::AccessCoins(const uint256& txid) const } } -bool CCoinsViewCache::HaveCoins(const uint256& txid) const +bool CCoinsViewCache::HaveCoins(const OutputHash& txid) const { CCoinsMap::const_iterator it = FetchCoins(txid); // We're using vtx.empty() instead of IsPruned here for performance reasons, diff --git a/divi/src/coins.h b/divi/src/coins.h index 27a96a0c6..5087df7d2 100644 --- a/divi/src/coins.h +++ b/divi/src/coins.h @@ -7,6 +7,7 @@ #define BITCOIN_COINS_H #include "compressor.h" +#include "OutputHash.h" #include "script/standard.h" #include "serialize.h" #include "uint256.h" @@ -244,9 +245,9 @@ class CCoinsKeyHasher * unordered_map will behave unpredictably if the custom hasher returns a * uint64_t, resulting in failures when syncing the chain (#4634). */ - size_t operator()(const uint256& key) const + size_t operator()(const OutputHash& key) const { - return key.GetHash(salt); + return key.GetValue().GetHash(salt); } }; @@ -262,7 +263,7 @@ struct CCoinsCacheEntry { CCoinsCacheEntry() : coins(), flags(0) {} }; -typedef boost::unordered_map CCoinsMap; +typedef boost::unordered_map CCoinsMap; struct CCoinsStats { int nHeight; @@ -282,11 +283,11 @@ class CCoinsView { public: //! Retrieve the CCoins (unspent transaction outputs) for a given txid - virtual bool GetCoins(const uint256& txid, CCoins& coins) const = 0; + virtual bool GetCoins(const OutputHash& txid, CCoins& coins) const = 0; //! Just check whether we have data for a given txid. //! This may (but cannot always) return true for fully spent transactions - virtual bool HaveCoins(const uint256& txid) const = 0; + virtual bool HaveCoins(const OutputHash& txid) const = 0; //! Retrieve the block hash whose state this CCoinsView currently represents virtual uint256 GetBestBlock() const = 0; @@ -312,8 +313,8 @@ class CCoinsViewBacked : public CCoinsView public: CCoinsViewBacked(); CCoinsViewBacked(CCoinsView* viewIn); - bool GetCoins(const uint256& txid, CCoins& coins) const override; - bool HaveCoins(const uint256& txid) const override; + bool GetCoins(const OutputHash& txid, CCoins& coins) const override; + bool HaveCoins(const OutputHash& txid) const override; uint256 GetBestBlock() const override; void SetBackend(CCoinsView& viewIn); void DettachBackend(); @@ -375,8 +376,8 @@ class CCoinsViewCache : public CCoinsViewBacked ~CCoinsViewCache(); // Standard CCoinsView methods - bool GetCoins(const uint256& txid, CCoins& coins) const override; - bool HaveCoins(const uint256& txid) const override; + bool GetCoins(const OutputHash& txid, CCoins& coins) const override; + bool HaveCoins(const OutputHash& txid) const override; uint256 GetBestBlock() const override; void SetBestBlock(const uint256& hashBlock); bool BatchWrite(CCoinsMap& mapCoins, const uint256& hashBlock) override; @@ -386,14 +387,14 @@ class CCoinsViewCache : public CCoinsViewBacked * more efficient than GetCoins. Modifications to other cache entries are * allowed while accessing the returned pointer. */ - const CCoins* AccessCoins(const uint256& txid) const; + const CCoins* AccessCoins(const OutputHash& txid) const; /** * Return a modifiable reference to a CCoins. If no entry with the given * txid exists, a new one is created. Simultaneous modifications are not * allowed. */ - CCoinsModifier ModifyCoins(const uint256& txid); + CCoinsModifier ModifyCoins(const OutputHash& txid); /** * Push the modifications applied to this cache to its base. @@ -426,8 +427,8 @@ class CCoinsViewCache : public CCoinsViewBacked friend class CCoinsModifier; private: - CCoinsMap::iterator FetchCoins(const uint256& txid); - CCoinsMap::const_iterator FetchCoins(const uint256& txid) const; + CCoinsMap::iterator FetchCoins(const OutputHash& txid); + CCoinsMap::const_iterator FetchCoins(const OutputHash& txid) const; }; #endif // BITCOIN_COINS_H diff --git a/divi/src/divi-tx.cpp b/divi/src/divi-tx.cpp index 7e0bd2601..c3888d6de 100644 --- a/divi/src/divi-tx.cpp +++ b/divi/src/divi-tx.cpp @@ -191,7 +191,7 @@ static void MutateTxAddInput(CMutableTransaction& tx, const string& strInput) string strTxid = strInput.substr(0, pos); if ((strTxid.size() != 64) || !IsHex(strTxid)) throw runtime_error("invalid TX input txid"); - uint256 txid(strTxid); + const OutputHash txid(uint256S(strTxid)); static const unsigned int minTxOutSz = 9; unsigned int nMaxSize = MAX_BLOCK_SIZE_LEGACY; @@ -378,7 +378,7 @@ static void MutateTxSign(CMutableTransaction& tx, const string& flagStr) if (!prevOut.checkObject(types)) throw runtime_error("prevtxs internal object typecheck fail"); - uint256 txid = ParseHashUV(prevOut["txid"], "txid"); + const OutputHash txid(ParseHashUV(prevOut["txid"], "txid")); int nOut = atoi(prevOut["vout"].getValStr()); if (nOut < 0) diff --git a/divi/src/init.cpp b/divi/src/init.cpp index 08925ad62..dcc9528d1 100644 --- a/divi/src/init.cpp +++ b/divi/src/init.cpp @@ -176,7 +176,7 @@ class CCoinsViewErrorCatcher : public CCoinsViewBacked { public: CCoinsViewErrorCatcher(CCoinsView* view) : CCoinsViewBacked(view) {} - bool GetCoins(const uint256& txid, CCoins& coins) const override + bool GetCoins(const OutputHash& txid, CCoins& coins) const override { try { return CCoinsViewBacked::GetCoins(txid, coins); diff --git a/divi/src/main.cpp b/divi/src/main.cpp index 25b77d97b..b8b453f98 100755 --- a/divi/src/main.cpp +++ b/divi/src/main.cpp @@ -3195,7 +3195,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } else if (strCommand == "tx" || strCommand == "dstx") { - std::vector vWorkQueue; + std::vector vWorkQueue; std::vector vEraseQueue; CTransaction tx; diff --git a/divi/src/masternode-payments.cpp b/divi/src/masternode-payments.cpp index ec0e71791..dd74255fa 100644 --- a/divi/src/masternode-payments.cpp +++ b/divi/src/masternode-payments.cpp @@ -56,7 +56,7 @@ namespace struct RankingCacheEntry { - using value_type = std::array; + using value_type = std::array; /** The scoring hash this is for, i.e. the key. */ uint256 scoringBlockHash; @@ -582,7 +582,7 @@ unsigned CMasternodePayments::GetMasternodeRank(const CTxIn& vin, const uint256& cacheEntry = rankingCache->Find(scoringBlockHash); if (cacheEntry == nullptr) { - std::vector> rankedNodes; + std::vector> rankedNodes; { LOCK(networkMessageManager_.cs); masternodeManager_.Check(); @@ -596,7 +596,7 @@ unsigned CMasternodePayments::GetMasternodeRank(const CTxIn& vin, const uint256& } std::sort(rankedNodes.begin(), rankedNodes.end(), - [] (const std::pair& a, const std::pair& b) + [] (const std::pair& a, const std::pair& b) { return a.first > b.first; }); diff --git a/divi/src/masternode.cpp b/divi/src/masternode.cpp index c4586ab94..1ef10a054 100644 --- a/divi/src/masternode.cpp +++ b/divi/src/masternode.cpp @@ -163,7 +163,7 @@ static uint256 CalculateScoreHelper(CHashWriter hashWritter, int round) // uint256 CMasternode::CalculateScore(const uint256& scoringBlockHash) const { - const uint256 aux = vin.prevout.hash + vin.prevout.n; + const uint256 aux = vin.prevout.hash.GetValue() + vin.prevout.n; const size_t nHashRounds = GetHashRoundsForTierMasternodes(static_cast(nTier)); CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); diff --git a/divi/src/masternodeconfig.cpp b/divi/src/masternodeconfig.cpp index f85af85b3..53b304e8c 100644 --- a/divi/src/masternodeconfig.cpp +++ b/divi/src/masternodeconfig.cpp @@ -10,13 +10,14 @@ #include #include #include +#include #include // clang-format on bool CMasternodeConfig::CMasternodeEntry::parseInputReference(COutPoint& outp) const { - outp.hash = uint256S(getTxHash()); + outp.hash = OutputHash(uint256S(getTxHash())); try { outp.n = std::stoi(getOutputIndex().c_str()); diff --git a/divi/src/primitives/transaction.cpp b/divi/src/primitives/transaction.cpp index 1f4a9c2cc..24f556529 100644 --- a/divi/src/primitives/transaction.cpp +++ b/divi/src/primitives/transaction.cpp @@ -18,7 +18,7 @@ COutPoint::COutPoint() { SetNull(); } -COutPoint::COutPoint(const uint256& hashIn, uint32_t nIn) +COutPoint::COutPoint(const OutputHash& hashIn, uint32_t nIn) : hash(hashIn), n(nIn) {} @@ -83,7 +83,7 @@ CTxIn::CTxIn(COutPoint prevoutIn, CScript scriptSigIn, uint32_t nSequenceIn) nSequence = nSequenceIn; } -CTxIn::CTxIn(const uint256& hashPrevTx, uint32_t nOut, CScript scriptSigIn, uint32_t nSequenceIn) +CTxIn::CTxIn(const OutputHash& hashPrevTx, uint32_t nOut, CScript scriptSigIn, uint32_t nSequenceIn) { prevout = COutPoint(hashPrevTx, nOut); scriptSig = scriptSigIn; diff --git a/divi/src/primitives/transaction.h b/divi/src/primitives/transaction.h index fb30da67a..5d0e5095a 100644 --- a/divi/src/primitives/transaction.h +++ b/divi/src/primitives/transaction.h @@ -8,6 +8,7 @@ #define BITCOIN_PRIMITIVES_TRANSACTION_H #include "amount.h" +#include "OutputHash.h" #include "script/script.h" #include "serialize.h" #include "uint256.h" @@ -18,11 +19,11 @@ class COutPoint { public: - uint256 hash; + OutputHash hash; uint32_t n; COutPoint(); - COutPoint(const uint256& hashIn, uint32_t nIn); + COutPoint(const OutputHash& hashIn, uint32_t nIn); ADD_SERIALIZE_METHODS; @@ -57,7 +58,7 @@ class CTxIn CTxIn(); explicit CTxIn(COutPoint prevoutIn, CScript scriptSigIn=CScript(), uint32_t nSequenceIn=std::numeric_limits::max()); - CTxIn(const uint256& hashPrevTx, uint32_t nOut, CScript scriptSigIn=CScript(), uint32_t nSequenceIn=std::numeric_limits::max()); + CTxIn(const OutputHash& hashPrevTx, uint32_t nOut, CScript scriptSigIn=CScript(), uint32_t nSequenceIn=std::numeric_limits::max()); ADD_SERIALIZE_METHODS; diff --git a/divi/src/rpcblockchain.cpp b/divi/src/rpcblockchain.cpp index e22fb0750..3bfa94d41 100644 --- a/divi/src/rpcblockchain.cpp +++ b/divi/src/rpcblockchain.cpp @@ -447,8 +447,8 @@ Value gettxout(const Array& params, bool fHelp) Object ret; - std::string strHash = params[0].get_str(); - uint256 hash(strHash); + const std::string strHash = params[0].get_str(); + const OutputHash hash(uint256S(strHash)); int n = params[1].get_int(); bool fMempool = true; if (params.size() > 2) diff --git a/divi/src/rpcmisc.cpp b/divi/src/rpcmisc.cpp index 9be2875a0..e28080afe 100644 --- a/divi/src/rpcmisc.cpp +++ b/divi/src/rpcmisc.cpp @@ -997,8 +997,8 @@ Value getspentinfo(const Array& params, bool fHelp) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid txid or index"); } - uint256 txid = ParseHashV(txidValue, "txid"); - int outputIndex = indexValue.get_int(); + const OutputHash txid(ParseHashV(txidValue, "txid")); + const int outputIndex = indexValue.get_int(); const CSpentIndexKey key(txid, outputIndex); CSpentIndexValue value; diff --git a/divi/src/rpcrawtransaction.cpp b/divi/src/rpcrawtransaction.cpp index e53ea5099..416e1dcc8 100644 --- a/divi/src/rpcrawtransaction.cpp +++ b/divi/src/rpcrawtransaction.cpp @@ -131,9 +131,9 @@ void TxToJSONExpanded(const CTransaction& tx, const uint256 hashBlock, Object& e // so we simply try looking up by both txid and bare txid as at // most one of them can match anyway. CSpentIndexValue spentInfo; - bool found = TransactionSearchIndexes::GetSpentIndex(fSpentIndex,pblocktree,mempool,CSpentIndexKey(txid, i), spentInfo); + bool found = TransactionSearchIndexes::GetSpentIndex(fSpentIndex, pblocktree, mempool, CSpentIndexKey(OutputHash(txid), i), spentInfo); if (!found) - found = TransactionSearchIndexes::GetSpentIndex(fSpentIndex,pblocktree,mempool,CSpentIndexKey(tx.GetBareTxid(), i), spentInfo); + found = TransactionSearchIndexes::GetSpentIndex(fSpentIndex, pblocktree, mempool, CSpentIndexKey(OutputHash(tx.GetBareTxid()), i), spentInfo); if (found) { out.push_back(Pair("spentTxId", spentInfo.txid.GetHex())); out.push_back(Pair("spentIndex", (int)spentInfo.inputIndex)); @@ -488,7 +488,7 @@ Value createrawtransaction(const Array& params, bool fHelp) for (const Value& input : inputs) { const Object& o = input.get_obj(); - uint256 txid = ParseHashO(o, "txid"); + const OutputHash txid(ParseHashO(o, "txid")); const Value& vout_v = find_value(o, "vout"); if (vout_v.type() != int_type) @@ -717,7 +717,7 @@ Value signrawtransaction(const Array& params, bool fHelp) view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view BOOST_FOREACH (const CTxIn& txin, mergedTx.vin) { - const uint256& prevHash = txin.prevout.hash; + const OutputHash& prevHash = txin.prevout.hash; CCoins coins; view.AccessCoins(prevHash); // this is certainly allowed to fail } @@ -757,7 +757,7 @@ Value signrawtransaction(const Array& params, bool fHelp) RPCTypeCheck(prevOut, map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type)); - uint256 txid = ParseHashO(prevOut, "txid"); + const OutputHash txid(ParseHashO(prevOut, "txid")); int nOut = find_value(prevOut, "vout").get_int(); if (nOut < 0) @@ -900,9 +900,9 @@ Value sendrawtransaction(const Array& params, bool fHelp) whether or not segwit light has been in effect for the transaction, we just try locating both txid and bare txid as at most one of them can match anyway. */ - const CCoins* existingCoins = view.AccessCoins(hashTx); + const CCoins* existingCoins = view.AccessCoins(OutputHash(hashTx)); if (existingCoins == nullptr) - existingCoins = view.AccessCoins(tx.GetBareTxid()); + existingCoins = view.AccessCoins(OutputHash(tx.GetBareTxid())); const bool fHaveChain = existingCoins && existingCoins->nHeight < 1000000000; static const CFeeRate& feeRate = FeeAndPriorityCalculator::instance().getMinimumRelayFeeRate(); if (!fHaveMempool && !fHaveChain) { diff --git a/divi/src/rpcwallet.cpp b/divi/src/rpcwallet.cpp index 06c7d251f..0f9f20e92 100644 --- a/divi/src/rpcwallet.cpp +++ b/divi/src/rpcwallet.cpp @@ -2589,7 +2589,7 @@ Value lockunspent(const Array& params, bool fHelp) if (nOutput < 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); - COutPoint outpt(uint256(txid), nOutput); + const COutPoint outpt(OutputHash(uint256(txid)), nOutput); if (fUnlock) pwalletMain->UnlockCoin(outpt); diff --git a/divi/src/spentindex.h b/divi/src/spentindex.h index 6a05effb2..1f5a34082 100644 --- a/divi/src/spentindex.h +++ b/divi/src/spentindex.h @@ -11,7 +11,7 @@ #include "serialize.h" struct CSpentIndexKey { - uint256 txid; + OutputHash txid; unsigned int outputIndex; ADD_SERIALIZE_METHODS; @@ -22,7 +22,7 @@ struct CSpentIndexKey { READWRITE(outputIndex); } - CSpentIndexKey(const uint256& t, unsigned int i) { + CSpentIndexKey(const OutputHash& t, unsigned int i) { txid = t; outputIndex = i; } diff --git a/divi/src/txdb.cpp b/divi/src/txdb.cpp index 685a9da7f..ecb06d27c 100644 --- a/divi/src/txdb.cpp +++ b/divi/src/txdb.cpp @@ -5,6 +5,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "txdb.h" +#include "OutputHash.h" #include "uint256.h" #include #include @@ -40,7 +41,7 @@ constexpr char DB_NAMEDFLAG = 'F'; } // anonymous namespace -void static BatchWriteCoins(CLevelDBBatch& batch, const uint256& hash, const CCoins& coins) +void static BatchWriteCoins(CLevelDBBatch& batch, const OutputHash& hash, const CCoins& coins) { if (coins.IsPruned()) batch.Erase(std::make_pair(DB_COINS, hash)); @@ -63,12 +64,12 @@ CCoinsViewDB::CCoinsViewDB( { } -bool CCoinsViewDB::GetCoins(const uint256& txid, CCoins& coins) const +bool CCoinsViewDB::GetCoins(const OutputHash& txid, CCoins& coins) const { return db.Read(std::make_pair(DB_COINS, txid), coins); } -bool CCoinsViewDB::HaveCoins(const uint256& txid) const +bool CCoinsViewDB::HaveCoins(const OutputHash& txid) const { return db.Exists(std::make_pair(DB_COINS, txid)); } diff --git a/divi/src/txdb.h b/divi/src/txdb.h index 33395f338..30318b98a 100644 --- a/divi/src/txdb.h +++ b/divi/src/txdb.h @@ -40,8 +40,8 @@ class CCoinsViewDB : public CCoinsView public: CCoinsViewDB(const BlockMap& blockIndicesByHash, size_t nCacheSize, bool fMemory = false, bool fWipe = false); - bool GetCoins(const uint256& txid, CCoins& coins) const override; - bool HaveCoins(const uint256& txid) const override; + bool GetCoins(const OutputHash& txid, CCoins& coins) const override; + bool HaveCoins(const OutputHash& txid) const override; uint256 GetBestBlock() const override; bool BatchWrite(CCoinsMap& mapCoins, const uint256& hashBlock) override; bool GetStats(CCoinsStats& stats) const override; diff --git a/divi/src/txmempool.cpp b/divi/src/txmempool.cpp index c4f329aa8..7b8af075a 100644 --- a/divi/src/txmempool.cpp +++ b/divi/src/txmempool.cpp @@ -38,9 +38,9 @@ class MempoolUtxoHasher : public TransactionUtxoHasher public: - uint256 GetUtxoHash(const CTransaction& tx) const override + OutputHash GetUtxoHash(const CTransaction& tx) const override { - return tx.GetHash(); + return OutputHash(tx.GetHash()); } }; @@ -76,7 +76,7 @@ CTxMemPool::~CTxMemPool() feePolicyEstimator.reset(); } -void CTxMemPool::pruneSpent(const uint256& hashTx, CCoins& coins) +void CTxMemPool::pruneSpent(const OutputHash& hashTx, CCoins& coins) { LOCK(cs); @@ -507,15 +507,15 @@ bool CTxMemPool::lookupBareTxid(const uint256& btxid, CTransaction& result) cons return true; } -bool CTxMemPool::lookupOutpoint(const uint256& hash, CTransaction& result) const +bool CTxMemPool::lookupOutpoint(const OutputHash& hash, CTransaction& result) const { /* The TransactionUtxoHasher can only tell us the txid to use once we know the transaction already. Thus we check both txid and bare txid in our index; if one of them matches, we then cross-check with the then-known transaction that it actually should hash to that UTXO. */ - if (lookup(hash, result) && utxoHasher->GetUtxoHash(result) == hash) + if (lookup(hash.GetValue(), result) && utxoHasher->GetUtxoHash(result) == hash) return true; - if (lookupBareTxid(hash, result) && utxoHasher->GetUtxoHash(result) == hash) + if (lookupBareTxid(hash.GetValue(), result) && utxoHasher->GetUtxoHash(result) == hash) return true; return false; @@ -607,7 +607,7 @@ void CTxMemPool::ClearPrioritisation(const uint256 hash) CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView* baseIn, CTxMemPool& mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) {} -bool CCoinsViewMemPool::GetCoins(const uint256& txid, CCoins& coins) const +bool CCoinsViewMemPool::GetCoins(const OutputHash& txid, CCoins& coins) const { // If an entry in the mempool exists, always return that one, as it's guaranteed to never // conflict with the underlying cache, and it cannot have pruned entries (as it contains full) @@ -620,7 +620,7 @@ bool CCoinsViewMemPool::GetCoins(const uint256& txid, CCoins& coins) const return (CCoinsViewBacked::GetCoins(txid, coins) && !coins.IsPruned()); } -bool CCoinsViewMemPool::HaveCoins(const uint256& txid) const +bool CCoinsViewMemPool::HaveCoins(const OutputHash& txid) const { CTransaction dummy; if (mempool.lookupOutpoint(txid, dummy)) @@ -628,7 +628,7 @@ bool CCoinsViewMemPool::HaveCoins(const uint256& txid) const return CCoinsViewBacked::HaveCoins(txid); } -bool CCoinsViewMemPool::GetCoinsAndPruneSpent(const uint256& txid,CCoins& coins) const +bool CCoinsViewMemPool::GetCoinsAndPruneSpent(const OutputHash& txid,CCoins& coins) const { LOCK(mempool.cs); if (!GetCoins(txid, coins)) diff --git a/divi/src/txmempool.h b/divi/src/txmempool.h index f2a288540..6438d243e 100644 --- a/divi/src/txmempool.h +++ b/divi/src/txmempool.h @@ -124,7 +124,7 @@ class CTxMemPool void removeConfirmedTransactions(const std::vector& vtx, unsigned int nBlockHeight, std::list& conflicts); void clear(); void queryHashes(std::vector& vtxid); - void pruneSpent(const uint256& hash, CCoins& coins); + void pruneSpent(const OutputHash& hash, CCoins& coins); unsigned int GetTransactionsUpdated() const; void AddTransactionsUpdated(unsigned int n); @@ -174,7 +174,7 @@ class CTxMemPool /** Looks up a transaction by its outpoint for spending, taking potential changes * from the raw txid (e.g. segwit light) into account. */ - bool lookupOutpoint(const uint256& hash, CTransaction& result) const; + bool lookupOutpoint(const OutputHash& hash, CTransaction& result) const; /** Estimate fee rate needed to get into the next nBlocks */ CFeeRate estimateFee(int nBlocks) const; @@ -198,9 +198,9 @@ class CCoinsViewMemPool : public CCoinsViewBacked public: CCoinsViewMemPool(CCoinsView* baseIn, CTxMemPool& mempoolIn); - bool GetCoins(const uint256& txid, CCoins& coins) const override; - bool HaveCoins(const uint256& txid) const override; - bool GetCoinsAndPruneSpent(const uint256& txid,CCoins& coins) const; + bool GetCoins(const OutputHash& txid, CCoins& coins) const override; + bool HaveCoins(const OutputHash& txid) const override; + bool GetCoinsAndPruneSpent(const OutputHash& txid, CCoins& coins) const; }; class CValidationState; bool SubmitTransactionToMempool(CTxMemPool& mempool, const CTransaction& tx); diff --git a/divi/src/wallet.cpp b/divi/src/wallet.cpp index 7569754c3..3d2018676 100644 --- a/divi/src/wallet.cpp +++ b/divi/src/wallet.cpp @@ -184,9 +184,9 @@ class WalletUtxoHasher : public TransactionUtxoHasher public: - uint256 GetUtxoHash(const CTransaction& tx) const override + OutputHash GetUtxoHash(const CTransaction& tx) const override { - return tx.GetHash(); + return OutputHash(tx.GetHash()); } }; @@ -504,7 +504,7 @@ CAmount CWallet::ComputeCredit(const CWalletTx& tx, const UtxoOwnershipFilter& f { const CAmount maxMoneyAllowedInOutput = Params().MaxMoneyOut(); CAmount nCredit = 0; - const uint256 hash = GetUtxoHash(tx); + const auto hash = GetUtxoHash(tx); for (unsigned int i = 0; i < tx.vout.size(); i++) { if( (creditFilterFlags & REQUIRE_UNSPENT) && IsSpent(tx,i)) continue; if( (creditFilterFlags & REQUIRE_UNLOCKED) && IsLockedCoin(hash,i)) continue; @@ -587,6 +587,12 @@ const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const LOCK(cs_wallet); return transactionRecord_->GetWalletTx(hash); } + +const CWalletTx* CWallet::GetWalletTx(const OutputHash& hash) const +{ + return GetWalletTx(hash.GetValue()); +} + std::vector CWallet::GetWalletTransactionReferences() const { LOCK(cs_wallet); @@ -1715,7 +1721,7 @@ bool CWallet::IsAvailableForSpending( return false; } - const uint256 hash = GetUtxoHash(*pcoin); + const auto hash = GetUtxoHash(*pcoin); if (IsSpent(*pcoin, i)) return false; @@ -2293,7 +2299,7 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) // Notify that old coins are spent { - std::set updated_hashes; + std::set updated_hashes; BOOST_FOREACH (const CTxIn& txin, wtxNew.vin) { // notify only once if (updated_hashes.find(txin.prevout.hash) != updated_hashes.end()) continue; @@ -2342,7 +2348,7 @@ std::pair CWallet::SendMoney( return {std::string(""),true}; } -uint256 CWallet::GetUtxoHash(const CMerkleTx& tx) const +OutputHash CWallet::GetUtxoHash(const CMerkleTx& tx) const { return utxoHasher->GetUtxoHash(tx); } @@ -2818,7 +2824,7 @@ void CWallet::UnlockAllCoins() setLockedCoins.clear(); } -bool CWallet::IsLockedCoin(const uint256& hash, unsigned int n) const +bool CWallet::IsLockedCoin(const OutputHash& hash, unsigned int n) const { AssertLockHeld(cs_wallet); // setLockedCoins COutPoint outpt(hash, n); diff --git a/divi/src/wallet.h b/divi/src/wallet.h index cc7f6e821..0ca4e602e 100644 --- a/divi/src/wallet.h +++ b/divi/src/wallet.h @@ -256,6 +256,7 @@ class CWallet : const CWalletTx* GetWalletTx(const uint256& hash) const; + const CWalletTx* GetWalletTx(const OutputHash& hash) const; std::vector GetWalletTransactionReferences() const; void RelayWalletTransaction(const CWalletTx& walletTransaction); @@ -285,13 +286,13 @@ class CWallet : CAmount& nValueRet); bool IsTrusted(const CWalletTx& walletTransaction) const; - bool IsLockedCoin(const uint256& hash, unsigned int n) const; + bool IsLockedCoin(const OutputHash& hash, unsigned int n) const; void LockCoin(const COutPoint& output); void UnlockCoin(const COutPoint& output); void UnlockAllCoins(); void ListLockedCoins(CoinVector& vOutpts); - uint256 GetUtxoHash(const CMerkleTx& tx) const override; + OutputHash GetUtxoHash(const CMerkleTx& tx) const override; /** Replaces the UTXO hasher used in the wallet, for testing purposes. */ void SetUtxoHasherForTesting(std::unique_ptr hasher); From 18d2b080276d64f30cce8b74861e8b3971ba5650 Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Tue, 15 Jun 2021 16:18:45 +0200 Subject: [PATCH 10/14] Update unit tests for OutputHash. Update also the unit tests for the OutputHash type. This is a separate commit to make the review easier (as reviewing the main part of the change in production code is separate). --- divi/src/test/ActiveMasternode_tests.cpp | 10 +-- divi/src/test/BareTxid_tests.cpp | 4 +- divi/src/test/BlockSignature_tests.cpp | 2 +- divi/src/test/DoS_tests.cpp | 6 +- divi/src/test/FakeWallet.cpp | 2 +- .../test/LotteryWinnersCalculatorTests.cpp | 4 +- divi/src/test/MockUtxoHasher.cpp | 10 +-- divi/src/test/MockUtxoHasher.h | 6 +- divi/src/test/ProofOfStake_tests.cpp | 20 +++--- .../test/SignatureSizeEstimation_tests.cpp | 4 +- divi/src/test/TestCoinsView.cpp | 4 +- .../test/UtxoCheckingAndUpdating_tests.cpp | 4 +- divi/src/test/VaultManager_tests.cpp | 64 +++++++++---------- divi/src/test/coins_tests.cpp | 14 ++-- divi/src/test/kernel_tests.cpp | 8 +-- divi/src/test/mempool_tests.cpp | 38 +++++------ divi/src/test/multisig_tests.cpp | 4 +- divi/src/test/script_CLTV_tests.cpp | 4 +- divi/src/test/script_P2SH_tests.cpp | 14 ++-- divi/src/test/script_tests.cpp | 2 +- divi/src/test/sighash_tests.cpp | 2 +- divi/src/test/transaction_tests.cpp | 16 ++--- divi/src/test/wallet_coinmanagement_tests.cpp | 12 ++-- 23 files changed, 127 insertions(+), 127 deletions(-) diff --git a/divi/src/test/ActiveMasternode_tests.cpp b/divi/src/test/ActiveMasternode_tests.cpp index 18667802e..e9fdb8401 100644 --- a/divi/src/test/ActiveMasternode_tests.cpp +++ b/divi/src/test/ActiveMasternode_tests.cpp @@ -70,7 +70,7 @@ BOOST_AUTO_TEST_CASE(willNotEnableMasternodeOnEmptyConfigurations) BOOST_AUTO_TEST_CASE(willEnableMasternodeOnMatchingUTXO) { - uint256 dummyHash = GetRandHash(); + const OutputHash dummyHash(GetRandHash()); uint32_t out = 0; CTxIn validTxIn (dummyHash, out); CService service; @@ -84,10 +84,10 @@ BOOST_AUTO_TEST_CASE(willNotEnableMasternodeOnMismatchedUTXO) { uint32_t out = 0; - uint256 correctDummyHash = GetRandHash(); + const OutputHash correctDummyHash(GetRandHash()); CTxIn validTxIn (correctDummyHash, out); - uint256 wrongDummyHash = GetRandHash(); + const OutputHash wrongDummyHash(GetRandHash()); CTxIn wrongTxIn (wrongDummyHash, out); @@ -112,7 +112,7 @@ BOOST_AUTO_TEST_CASE(willResetStatusToSyncInProgressWhenChainSyncIsRequired) { uint256 dummyHash = GetRandHash(); uint32_t out = 0; - CTxIn validTxIn (dummyHash, out); + CTxIn validTxIn (OutputHash(dummyHash), out); CService service; AddDummyConfiguration(validTxIn, service); @@ -131,4 +131,4 @@ BOOST_AUTO_TEST_CASE(willCannotResetStatusOfInactiveMasternodeWhenChainSyncIsReq BOOST_CHECK(activeMasternode_->status == ACTIVE_MASTERNODE_STARTED); } -BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file +BOOST_AUTO_TEST_SUITE_END() diff --git a/divi/src/test/BareTxid_tests.cpp b/divi/src/test/BareTxid_tests.cpp index 6c20d94d3..8d76328b2 100644 --- a/divi/src/test/BareTxid_tests.cpp +++ b/divi/src/test/BareTxid_tests.cpp @@ -35,8 +35,8 @@ class BareTxidTestFixture mtx.nVersion = 1; mtx.nLockTime = 1234567890; - mtx.vin.emplace_back (HashStr ("in 1"), 4, CScript () << OP_TRUE << OP_FALSE); - mtx.vin.emplace_back (HashStr ("in 2"), 2, CScript () << OP_DUP); + mtx.vin.emplace_back (OutputHash (HashStr ("in 1")), 4, CScript () << OP_TRUE << OP_FALSE); + mtx.vin.emplace_back (OutputHash (HashStr ("in 2")), 2, CScript () << OP_DUP); mtx.vout.emplace_back (1 * COIN, CScript () << OP_TRUE); mtx.vout.emplace_back (0, CScript () << OP_META << HashToVec ("data")); diff --git a/divi/src/test/BlockSignature_tests.cpp b/divi/src/test/BlockSignature_tests.cpp index 07f3932ee..164c156b5 100644 --- a/divi/src/test/BlockSignature_tests.cpp +++ b/divi/src/test/BlockSignature_tests.cpp @@ -78,7 +78,7 @@ class BlockSignatureTestFixture CScript outputScript = (isP2SH)? ConvertToP2SH(redeemScript):redeemScript; vaultKeyStore.AddCScript(redeemScript); - coinstake.vin.emplace_back(uint256S("0x25"), 0, createDummyVaultScriptSig(redeemScript)); + coinstake.vin.emplace_back(OutputHash(uint256S("0x25")), 0, createDummyVaultScriptSig(redeemScript)); coinstake.vout.emplace_back(0, outputScript); block.vtx.emplace_back(); diff --git a/divi/src/test/DoS_tests.cpp b/divi/src/test/DoS_tests.cpp index bb9816bb5..6fbf1f5d2 100644 --- a/divi/src/test/DoS_tests.cpp +++ b/divi/src/test/DoS_tests.cpp @@ -144,7 +144,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans) CMutableTransaction tx; tx.vin.resize(1); tx.vin[0].prevout.n = 0; - tx.vin[0].prevout.hash = GetRandHash(); + tx.vin[0].prevout.hash = OutputHash(GetRandHash()); tx.vin[0].scriptSig << OP_1; tx.vout.resize(1); tx.vout[0].nValue = 1*CENT; @@ -161,7 +161,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans) CMutableTransaction tx; tx.vin.resize(1); tx.vin[0].prevout.n = 0; - tx.vin[0].prevout.hash = txPrev.GetHash(); + tx.vin[0].prevout.hash = OutputHash(txPrev.GetHash()); tx.vout.resize(1); tx.vout[0].nValue = 1*CENT; tx.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID()); @@ -183,7 +183,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans) for (unsigned int j = 0; j < tx.vin.size(); j++) { tx.vin[j].prevout.n = j; - tx.vin[j].prevout.hash = txPrev.GetHash(); + tx.vin[j].prevout.hash = OutputHash(txPrev.GetHash()); } SignSignature(keystore, txPrev, tx, 0); // Re-use same signature for other inputs diff --git a/divi/src/test/FakeWallet.cpp b/divi/src/test/FakeWallet.cpp index 98ae035db..2a795c401 100644 --- a/divi/src/test/FakeWallet.cpp +++ b/divi/src/test/FakeWallet.cpp @@ -71,7 +71,7 @@ CMutableTransaction createDefaultTransaction(const CScript& defaultScript, unsig tx.vout[index].scriptPubKey = defaultScript; tx.vin.resize(1); // Avoid flagging as a coinbase tx - tx.vin[0].prevout = COutPoint(GetRandHash(),static_cast(GetRand(100)) ); + tx.vin[0].prevout = COutPoint(OutputHash(GetRandHash()), static_cast(GetRand(100)) ); return tx; } diff --git a/divi/src/test/LotteryWinnersCalculatorTests.cpp b/divi/src/test/LotteryWinnersCalculatorTests.cpp index d7c14e819..b1865221a 100644 --- a/divi/src/test/LotteryWinnersCalculatorTests.cpp +++ b/divi/src/test/LotteryWinnersCalculatorTests.cpp @@ -78,7 +78,7 @@ class LotteryWinnersCalculatorTestFixture {// Avoid flagging as a coinbase tx const uint256 randomHash = uint256S("4f5e1dcf6b28438ecb4f92c37f72bc430055fc91651f3dbc22050eb93164c579"); constexpr uint32_t randomIndex = 42; - tx.vin[0].prevout = COutPoint(randomHash,randomIndex); + tx.vin[0].prevout = COutPoint(OutputHash(randomHash), randomIndex); } assert(CTransaction(tx).IsCoinStake()); return tx; @@ -231,4 +231,4 @@ BOOST_AUTO_TEST_CASE(willRemoveLowestScoringDuplicateIfNewWinnersAreAvailable) } } } -BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file +BOOST_AUTO_TEST_SUITE_END() diff --git a/divi/src/test/MockUtxoHasher.cpp b/divi/src/test/MockUtxoHasher.cpp index 2170dd638..8e2eafaa9 100644 --- a/divi/src/test/MockUtxoHasher.cpp +++ b/divi/src/test/MockUtxoHasher.cpp @@ -3,24 +3,24 @@ #include "hash.h" #include "primitives/transaction.h" -uint256 MockUtxoHasher::Add(const CTransaction& tx) +OutputHash MockUtxoHasher::Add(const CTransaction& tx) { ++cnt; - const uint256 value = Hash(&cnt, (&cnt) + 1); + const OutputHash value(Hash(&cnt, (&cnt) + 1)); customHashes.emplace(tx.GetHash(), value); return value; } void MockUtxoHasher::UseBareTxid(const CTransaction& tx) { - customHashes.emplace(tx.GetHash(), tx.GetBareTxid()); + customHashes.emplace(tx.GetHash(), OutputHash(tx.GetBareTxid())); } -uint256 MockUtxoHasher::GetUtxoHash(const CTransaction& tx) const +OutputHash MockUtxoHasher::GetUtxoHash(const CTransaction& tx) const { const uint256 txid = tx.GetHash(); const auto mit = customHashes.find(txid); if (mit != customHashes.end()) return mit->second; - return txid; + return OutputHash(txid); } diff --git a/divi/src/test/MockUtxoHasher.h b/divi/src/test/MockUtxoHasher.h index 9ea8f0ade..2ecf11e76 100644 --- a/divi/src/test/MockUtxoHasher.h +++ b/divi/src/test/MockUtxoHasher.h @@ -18,7 +18,7 @@ class MockUtxoHasher : public TransactionUtxoHasher private: /** Custom hashes to return for given txid's. */ - std::map customHashes; + std::map customHashes; /** Internal counter to produce unique fake hashes. */ unsigned cnt = 0; @@ -30,13 +30,13 @@ class MockUtxoHasher : public TransactionUtxoHasher /** Marks the given transaction for returning a custom hash. The hash * is generated uniquely and returned from the function. */ - uint256 Add(const CTransaction& tx); + OutputHash Add(const CTransaction& tx); /** Marks the given transaction for using the bare txid rather than * the normal txid. */ void UseBareTxid(const CTransaction& tx); - uint256 GetUtxoHash(const CTransaction& tx) const override; + OutputHash GetUtxoHash(const CTransaction& tx) const override; }; diff --git a/divi/src/test/ProofOfStake_tests.cpp b/divi/src/test/ProofOfStake_tests.cpp index dbf086fe7..1950a7b34 100644 --- a/divi/src/test/ProofOfStake_tests.cpp +++ b/divi/src/test/ProofOfStake_tests.cpp @@ -42,7 +42,7 @@ BOOST_AUTO_TEST_CASE(onlyHashesAFixedNumberOfTimesIfCalculatorRepeatedlyFails) unsigned chainTipDifficulty = 0x1000001; unsigned transactionTimeStart = blockTimeOfFirstUTXOConfirmation + 60*60*60; unsigned transactionTime = transactionTimeStart; - COutPoint utxo(GetRandHash(),GetRandInt(10)); + COutPoint utxo(OutputHash(GetRandHash()), GetRandInt(10)); uint256 blockHash = GetRandHash(); StakingData stakingData(chainTipDifficulty, blockTimeOfFirstUTXOConfirmation, blockHash, utxo, 0*COIN, 0); @@ -105,7 +105,7 @@ BOOST_AUTO_TEST_CASE(willEnsureBackwardCompatibilityWithMainnetHashproofs) unsigned(470026099), unsigned(1538645320), uint256S("967b03e3c1daf39633ed73ffb29abfcab9ae5b384dc5b95dabee0890bf8b4546"), - COutPoint(uint256S("4266403b499375917920311b1af704805d3fa2d6d6f4e3217026618028423607"), 1), + COutPoint(OutputHash(uint256S("4266403b499375917920311b1af704805d3fa2d6d6f4e3217026618028423607")), 1), CAmount(62542750000000), uint256("acf49c06030a7a76059a25b174dc7adcdc5f4ad36c91b564c585743af4829f7a") }, @@ -117,7 +117,7 @@ BOOST_AUTO_TEST_CASE(willEnsureBackwardCompatibilityWithMainnetHashproofs) unsigned(469856526), unsigned(1539089488), uint256S("5cf4c9c2cf5b7ce030d39bb8b322f4b7f60bfe3408c48071e88cbe82d1b0c05e"), - COutPoint(uint256S("9e6dea178ac919781836e19fd773fda9788a59b3ff0fc7daddaccaa880f55396"),1), + COutPoint(OutputHash(uint256S("9e6dea178ac919781836e19fd773fda9788a59b3ff0fc7daddaccaa880f55396")), 1), CAmount(9647175000000), uint256S("d4504de09669961a5d483070a0b4a0bd2231eda531b360ea5992c3cedaf096a0") }, @@ -129,7 +129,7 @@ BOOST_AUTO_TEST_CASE(willEnsureBackwardCompatibilityWithMainnetHashproofs) unsigned(469821893), unsigned(1539779674), uint256S("854dfc8fca75d2ff38b1d7eaa3d049d14f5c2756ae9e385faeef7b616aebfcb8"), - COutPoint(uint256S("91d6a2ecdc9d7afeec1b9ac711046188ea15022a439a5ae4b95b8ce725e45144"),2), + COutPoint(OutputHash(uint256S("91d6a2ecdc9d7afeec1b9ac711046188ea15022a439a5ae4b95b8ce725e45144")), 2), CAmount(7536149207500), uint256S("170781f60c8e5a7705a55d42272263b017df80d8c12a6eb7af6d74f5ba139cac") }, @@ -141,7 +141,7 @@ BOOST_AUTO_TEST_CASE(willEnsureBackwardCompatibilityWithMainnetHashproofs) unsigned(453551099), unsigned(1580666056), uint256S("dec4fd2e49eda04dae3bb36c4eea0c57f677a930bb7dcbea8d9957a90b11464c"), - COutPoint(uint256S("01dfa586a601481b66309b574bed91eccb0921c6faf908538b935e677a338b90"),1), + COutPoint(OutputHash(uint256S("01dfa586a601481b66309b574bed91eccb0921c6faf908538b935e677a338b90")), 1), CAmount(9126800000000), uint256S("d397d5a4e387246ba3bc1e8c22c0dcf7d00da3dc8e78e63648ee31de88e83b0c") }, @@ -153,7 +153,7 @@ BOOST_AUTO_TEST_CASE(willEnsureBackwardCompatibilityWithMainnetHashproofs) unsigned(453485185), unsigned(1586034307), uint256S("9cc08403bfeac16eb293b848d619639f52a693d13f00f0188c1efeb85fff332c"), - COutPoint(uint256S("47b3248311ada187ad01b25c4274289224fc16e185fa4427fa0dc62cde426913"),1), + COutPoint(OutputHash(uint256S("47b3248311ada187ad01b25c4274289224fc16e185fa4427fa0dc62cde426913")), 1), CAmount(5846400000000), uint256S("79808b1228f217c55ab46b52b7c5b120afeed598cb86bd8d7062bb6b1d90da4e") }, @@ -165,7 +165,7 @@ BOOST_AUTO_TEST_CASE(willEnsureBackwardCompatibilityWithMainnetHashproofs) unsigned(453338064), unsigned(1598487374), uint256S("e5fd3874ca56174d611c8925785a0dda728a4160b59ab777644e7a17500576d4"), - COutPoint(uint256S("d17d0226b20b1853b6ad50e73f132a1bd1ce1b5fa08db17c0cbbc93b82619da1"),1), + COutPoint(OutputHash(uint256S("d17d0226b20b1853b6ad50e73f132a1bd1ce1b5fa08db17c0cbbc93b82619da1")), 1), CAmount(1445296875000), uint256S("25f7f482cbf34cd7da9d5db0e3b633c8c0abe54e0de1ef96e97ba15e8713e984") }, @@ -177,7 +177,7 @@ BOOST_AUTO_TEST_CASE(willEnsureBackwardCompatibilityWithMainnetHashproofs) unsigned(453369958), unsigned(1603300997), uint256S("1f41f84a8aa151b900c66ba4c9423f7eef93bd076c030af54fa1dae1d238cfa0"), - COutPoint(uint256S("b5071db8b245780b77ecc6d4423ed5e8465b14065d19c394ec14ff0b5fde4d3a"),1), + COutPoint(OutputHash(uint256S("b5071db8b245780b77ecc6d4423ed5e8465b14065d19c394ec14ff0b5fde4d3a")), 1), CAmount(5557600000000), uint256S("bd04a024f2f3ad91e694d4c70c6a983f1e73bb7803333d0406e9c314d6c265b7") }, @@ -189,7 +189,7 @@ BOOST_AUTO_TEST_CASE(willEnsureBackwardCompatibilityWithMainnetHashproofs) unsigned(453347746), unsigned(1599860645), uint256S("4d2597aa8ff30a0f0f82466e1dfd7603d8f928e08c8887597e0e0524ae293e5c"), - COutPoint(uint256S("1f5d59023369ac9f32d25de4645cd4a1d101911a01514a1a854a6518795f4805"),1), + COutPoint(OutputHash(uint256S("1f5d59023369ac9f32d25de4645cd4a1d101911a01514a1a854a6518795f4805")), 1), CAmount(1699450000000), uint256S("2dfcd9caa2558c41148a13b67117197289863a887480825646acf5225bcc0156") }, @@ -216,4 +216,4 @@ BOOST_AUTO_TEST_CASE(willEnsureBackwardCompatibilityWithMainnetHashproofs) } } -BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file +BOOST_AUTO_TEST_SUITE_END() diff --git a/divi/src/test/SignatureSizeEstimation_tests.cpp b/divi/src/test/SignatureSizeEstimation_tests.cpp index 4ed7461f0..9c5011055 100644 --- a/divi/src/test/SignatureSizeEstimation_tests.cpp +++ b/divi/src/test/SignatureSizeEstimation_tests.cpp @@ -26,7 +26,7 @@ class SignatureSizeTestFixture CMutableTransaction getSampleTransaction() { CMutableTransaction sampleTransaction; - sampleTransaction.vin.emplace_back(uint256S("0x8b4bdd6fd8220ca956938d214cbd4635bfaacc663f53ad8bda5e434b9dc647fe"),1); + sampleTransaction.vin.emplace_back(OutputHash(uint256S("0x8b4bdd6fd8220ca956938d214cbd4635bfaacc663f53ad8bda5e434b9dc647fe")), 1); return sampleTransaction; } void createKeys(unsigned numberOfKeys, bool compressedKey = true) @@ -254,4 +254,4 @@ BOOST_AUTO_TEST_CASE(willRecoverCorrectSignatureSizeForP2SHScriptsWhenScriptsAre } -BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file +BOOST_AUTO_TEST_SUITE_END() diff --git a/divi/src/test/TestCoinsView.cpp b/divi/src/test/TestCoinsView.cpp index 3aa992b45..22df1dc01 100644 --- a/divi/src/test/TestCoinsView.cpp +++ b/divi/src/test/TestCoinsView.cpp @@ -9,12 +9,12 @@ class EmptyCoinsView : public CCoinsView public: - bool GetCoins(const uint256& txid, CCoins& coins) const override + bool GetCoins(const OutputHash& txid, CCoins& coins) const override { return false; } - bool HaveCoins(const uint256& txid) const override + bool HaveCoins(const OutputHash& txid) const override { return false; } diff --git a/divi/src/test/UtxoCheckingAndUpdating_tests.cpp b/divi/src/test/UtxoCheckingAndUpdating_tests.cpp index a185fe470..13be8cd8d 100644 --- a/divi/src/test/UtxoCheckingAndUpdating_tests.cpp +++ b/divi/src/test/UtxoCheckingAndUpdating_tests.cpp @@ -35,8 +35,8 @@ BOOST_AUTO_TEST_CASE(addsCorrectOutputs) UpdateCoinsWithTransaction(tx1, coins, txundo, utxoHasher, 101); UpdateCoinsWithTransaction(tx2, coins, txundo, utxoHasher, 102); - BOOST_CHECK(coins.HaveCoins(tx1.GetHash())); - BOOST_CHECK(!coins.HaveCoins(tx2.GetHash())); + BOOST_CHECK(coins.HaveCoins(OutputHash(tx1.GetHash()))); + BOOST_CHECK(!coins.HaveCoins(OutputHash(tx2.GetHash()))); BOOST_CHECK(coins.HaveCoins(id2)); } diff --git a/divi/src/test/VaultManager_tests.cpp b/divi/src/test/VaultManager_tests.cpp index 71885a84b..2318cf536 100644 --- a/divi/src/test/VaultManager_tests.cpp +++ b/divi/src/test/VaultManager_tests.cpp @@ -192,7 +192,7 @@ BOOST_AUTO_TEST_CASE(willDiscountSpentUTXOs) manager->addTransaction(tx,&blockMiningFirstTx, true); CMutableTransaction otherTx; - otherTx.vin.emplace_back( COutPoint(utxoHasher.GetUtxoHash(tx), 1u) ); + otherTx.vin.emplace_back( COutPoint(OutputHash(utxoHasher.GetUtxoHash(tx)), 1u) ); otherTx.vout.push_back(CTxOut(100,managedScript)); otherTx.vout.push_back(CTxOut(100,managedScript)); otherTx.vout.push_back(CTxOut(100,managedScript)); @@ -377,7 +377,7 @@ BOOST_AUTO_TEST_CASE(willHaveUTXOCountDiminishIfThirdPartySpendsScript) CScript otherScript = scriptGenerator(10); CMutableTransaction otherTx; - otherTx.vin.emplace_back( COutPoint(tx.GetHash(), 1u) ); + otherTx.vin.emplace_back( COutPoint(OutputHash(tx.GetHash()), 1u) ); otherTx.vout.push_back(CTxOut(100,otherScript)); otherTx.vout.push_back(CTxOut(100,otherScript)); otherTx.vout.push_back(CTxOut(100,otherScript)); @@ -403,8 +403,8 @@ BOOST_AUTO_TEST_CASE(willNotRecoverDepositUTXOsAfterReorg) CScript otherScript = scriptGenerator(10); CMutableTransaction otherTx; - otherTx.vin.emplace_back( COutPoint(tx.GetHash(), 0u) ); - otherTx.vin.emplace_back( COutPoint(tx.GetHash(), 1u) ); + otherTx.vin.emplace_back( COutPoint(OutputHash(tx.GetHash()), 0u) ); + otherTx.vin.emplace_back( COutPoint(OutputHash(tx.GetHash()), 1u) ); otherTx.vout.push_back(CTxOut(100,managedScript)); otherTx.vout.push_back(CTxOut(100,managedScript)); otherTx.vout.push_back(CTxOut(100,managedScript)); @@ -439,8 +439,8 @@ BOOST_AUTO_TEST_CASE(willRecoverPreviouslyStakedUTXOsAfterReorg) CScript otherScript = scriptGenerator(10); CMutableTransaction otherTx; - otherTx.vin.emplace_back( COutPoint(tx.GetHash(), 0u) ); - otherTx.vin.emplace_back( COutPoint(tx.GetHash(), 1u) ); + otherTx.vin.emplace_back( COutPoint(OutputHash(tx.GetHash()), 0u) ); + otherTx.vin.emplace_back( COutPoint(OutputHash(tx.GetHash()), 1u) ); otherTx.vout.emplace_back(); otherTx.vout.back().SetEmpty(); otherTx.vout.push_back(CTxOut(100,managedScript)); @@ -499,7 +499,7 @@ BOOST_AUTO_TEST_CASE(willRecordInTheWalletTxWetherTransactionWasADeposit) CScript otherScript = scriptGenerator(10); CMutableTransaction otherTx; - otherTx.vin.emplace_back( COutPoint(tx.GetHash(), 1u) ); + otherTx.vin.emplace_back( COutPoint(OutputHash(tx.GetHash()), 1u) ); otherTx.vout.push_back(CTxOut(100,otherScript)); otherTx.vout.push_back(CTxOut(100,otherScript)); otherTx.vout.push_back(CTxOut(100,otherScript)); @@ -529,8 +529,8 @@ BOOST_AUTO_TEST_CASE(willStopRecognizingUTXOsAsManagedWhenNotAllInputsAreKnown) BOOST_CHECK_EQUAL(manager->getManagedUTXOs().size(),1u); CMutableTransaction otherTx; - otherTx.vin.emplace_back( COutPoint(tx.GetHash(), 0u) ); - otherTx.vin.emplace_back( COutPoint(dummyTransaction.GetHash(), 0u) ); + otherTx.vin.emplace_back( COutPoint(OutputHash(tx.GetHash()), 0u) ); + otherTx.vin.emplace_back( COutPoint(OutputHash(dummyTransaction.GetHash()), 0u) ); otherTx.vout.push_back(CTxOut(50,managedScript)); CBlock blockMiningSecondTx = getBlockToMineTransaction(otherTx); manager->addTransaction(otherTx,&blockMiningSecondTx, false); @@ -556,8 +556,8 @@ BOOST_AUTO_TEST_CASE(willStopRecognizingUTXOsAsManagedWhenNotAllInputsAreManaged BOOST_CHECK_EQUAL(manager->getManagedUTXOs().size(),1u); CMutableTransaction otherTx; - otherTx.vin.emplace_back( COutPoint(tx.GetHash(), 0u) ); - otherTx.vin.emplace_back( COutPoint(dummyTransaction.GetHash(), 0u) ); + otherTx.vin.emplace_back( COutPoint(OutputHash(tx.GetHash()), 0u) ); + otherTx.vin.emplace_back( COutPoint(OutputHash(dummyTransaction.GetHash()), 0u) ); otherTx.vout.push_back(CTxOut(50,managedScript)); CBlock blockMiningSecondTx = getBlockToMineTransaction(otherTx); manager->addTransaction(otherTx,&blockMiningSecondTx, false); @@ -587,8 +587,8 @@ BOOST_AUTO_TEST_CASE(willIgnoreManagedUTXOsIfNotSpentByCoinstakeNorDeposited) BOOST_CHECK_EQUAL(manager->getManagedUTXOs().size(),4u); CMutableTransaction spendingTx; - spendingTx.vin.emplace_back( COutPoint(tx.GetHash(), 0u) ); - spendingTx.vin.emplace_back( COutPoint(otherTx.GetHash(), 0u) ); + spendingTx.vin.emplace_back( COutPoint(OutputHash(tx.GetHash()), 0u) ); + spendingTx.vin.emplace_back( COutPoint(OutputHash(otherTx.GetHash()), 0u) ); spendingTx.vout.push_back(CTxOut(50,managedScript)); spendingTx.vout.push_back(CTxOut(50,managedScript)); spendingTx.vout.push_back(CTxOut(50,managedScript)); @@ -599,8 +599,8 @@ BOOST_AUTO_TEST_CASE(willIgnoreManagedUTXOsIfNotSpentByCoinstakeNorDeposited) BOOST_CHECK_EQUAL(manager->getManagedUTXOs().size(),2u); CMutableTransaction secondSpendingTx; - secondSpendingTx.vin.emplace_back( COutPoint(tx.GetHash(), 1u) ); - secondSpendingTx.vin.emplace_back( COutPoint(otherTx.GetHash(), 1u) ); + secondSpendingTx.vin.emplace_back( COutPoint(OutputHash(tx.GetHash()), 1u) ); + secondSpendingTx.vin.emplace_back( COutPoint(OutputHash(otherTx.GetHash()), 1u) ); secondSpendingTx.vout.emplace_back(); secondSpendingTx.vout.back().SetEmpty(); secondSpendingTx.vout.push_back(CTxOut(50,managedScript)); @@ -631,7 +631,7 @@ BOOST_AUTO_TEST_CASE(willAcceptDepositBasedUTXOsEvenIfInputsArentKnown) CBlock blockMiningDummyTx = getBlockToMineTransaction(dummyTransaction); CMutableTransaction tx; - tx.vin.emplace_back( COutPoint(dummyTransaction.GetHash(), 0u) ); + tx.vin.emplace_back( COutPoint(OutputHash(dummyTransaction.GetHash()), 0u) ); tx.vout.push_back(CTxOut(100,managedScript)); CBlock blockMiningFirstTx = getBlockToMineTransaction(tx); manager->addTransaction(tx,&blockMiningFirstTx, true); @@ -649,7 +649,7 @@ BOOST_AUTO_TEST_CASE(willAllowUpdatingUTXOsToDepositStatus) CBlock blockMiningDummyTx = getBlockToMineTransaction(dummyTransaction); CMutableTransaction tx; - tx.vin.emplace_back( COutPoint(dummyTransaction.GetHash(), 0u) ); + tx.vin.emplace_back( COutPoint(OutputHash(dummyTransaction.GetHash()), 0u) ); tx.vout.push_back(CTxOut(100,managedScript)); CBlock blockMiningFirstTx = getBlockToMineTransaction(tx); manager->addTransaction(tx,&blockMiningFirstTx, false); @@ -692,7 +692,7 @@ BOOST_AUTO_TEST_CASE(willUpdatingDepositStatusWillPersist) CBlock blockMiningDummyTx = getBlockToMineTransaction(dummyTransaction); CMutableTransaction tx; - tx.vin.emplace_back( COutPoint(dummyTransaction.GetHash(), 0u) ); + tx.vin.emplace_back( COutPoint(OutputHash(dummyTransaction.GetHash()), 0u) ); tx.vout.push_back(CTxOut(100,managedScript)); CBlock blockMiningFirstTx = getBlockToMineTransaction(tx); manager->addTransaction(tx,&blockMiningFirstTx, false); @@ -715,7 +715,7 @@ BOOST_AUTO_TEST_CASE(willRecordTransactionsSpendingDeposits) manager->addTransaction(fundingTransaction,&blockMiningFundingTx, true); CMutableTransaction tx; - tx.vin.emplace_back( COutPoint(fundingTransaction.GetHash(), 0u) ); + tx.vin.emplace_back( COutPoint(OutputHash(fundingTransaction.GetHash()), 0u) ); tx.vout.push_back(CTxOut(100,managedScript)); CBlock blockMiningFirstTx = getBlockToMineTransaction(tx); manager->addTransaction(tx,&blockMiningFirstTx, false); @@ -736,7 +736,7 @@ BOOST_AUTO_TEST_CASE(willNotRecordADepositTransactionThatIsntExplicitlyAdded) CBlock blockMiningDummyTx = getBlockToMineTransaction(fundingTransaction); CMutableTransaction tx; - tx.vin.emplace_back( COutPoint(fundingTransaction.GetHash(), 0u) ); + tx.vin.emplace_back( COutPoint(OutputHash(fundingTransaction.GetHash()), 0u) ); tx.vout.push_back(CTxOut(100,managedScript)); CBlock blockMiningFirstTx = getBlockToMineTransaction(tx); manager->addTransaction(tx,&blockMiningFirstTx, false); @@ -756,7 +756,7 @@ BOOST_AUTO_TEST_CASE(willIgnoreCoinstakeTransactionWithSingleUnamangedInput) CBlock blockMiningDummyTx = getBlockToMineTransaction(dummyTransaction); CMutableTransaction otherTx; - otherTx.vin.emplace_back( COutPoint(dummyTransaction.GetHash(), 0u) ); + otherTx.vin.emplace_back( COutPoint(OutputHash(dummyTransaction.GetHash()), 0u) ); otherTx.vout.emplace_back(); otherTx.vout.back().SetEmpty(); otherTx.vout.push_back(CTxOut(100,managedScript)); @@ -789,9 +789,9 @@ BOOST_AUTO_TEST_CASE(willIgnoreOutputsInCoinstakeWithAtLeastOneUnamangedInput) manager->addTransaction(fundingTransaction,&blockMiningFundingTx, true); CMutableTransaction otherTx; - otherTx.vin.emplace_back( COutPoint(fundingTransaction.GetHash(), 0u) ); - otherTx.vin.emplace_back( COutPoint(dummyTransaction.GetHash(), 0u) ); - otherTx.vin.emplace_back( COutPoint(fundingTransaction.GetHash(), 1u) ); + otherTx.vin.emplace_back( COutPoint(OutputHash(fundingTransaction.GetHash()), 0u) ); + otherTx.vin.emplace_back( COutPoint(OutputHash(dummyTransaction.GetHash()), 0u) ); + otherTx.vin.emplace_back( COutPoint(OutputHash(fundingTransaction.GetHash()), 1u) ); otherTx.vout.emplace_back(); otherTx.vout.back().SetEmpty(); otherTx.vout.push_back(CTxOut(100,managedScript)); @@ -814,8 +814,8 @@ BOOST_AUTO_TEST_CASE(willIgnoreOutputsFromCoinstakeInLotteryBlockByDefault) manager->addTransaction(fundingTransaction,&blockMiningFundingTx, true); CMutableTransaction otherTx; - otherTx.vin.emplace_back( COutPoint(fundingTransaction.GetHash(), 0u) ); - otherTx.vin.emplace_back( COutPoint(fundingTransaction.GetHash(), 1u) ); + otherTx.vin.emplace_back( COutPoint(OutputHash(fundingTransaction.GetHash()), 0u) ); + otherTx.vin.emplace_back( COutPoint(OutputHash(fundingTransaction.GetHash()), 1u) ); otherTx.vout.emplace_back(); otherTx.vout.back().SetEmpty(); otherTx.vout.push_back(CTxOut(100,managedScript)); @@ -839,8 +839,8 @@ BOOST_AUTO_TEST_CASE(willRecordCoinstakeTransactionWithOnlyMangedInputs) manager->addTransaction(fundingTransaction,&blockMiningFundingTx, true); CMutableTransaction otherTx; - otherTx.vin.emplace_back( COutPoint(fundingTransaction.GetHash(), 0u) ); - otherTx.vin.emplace_back( COutPoint(fundingTransaction.GetHash(), 1u) ); + otherTx.vin.emplace_back( COutPoint(OutputHash(fundingTransaction.GetHash()), 0u) ); + otherTx.vin.emplace_back( COutPoint(OutputHash(fundingTransaction.GetHash()), 1u) ); otherTx.vout.emplace_back(); otherTx.vout.back().SetEmpty(); otherTx.vout.push_back(CTxOut(100,managedScript)); @@ -864,7 +864,7 @@ BOOST_AUTO_TEST_CASE(willNotAddNonDepositUnconfirmedTransactions) CBlock blockMiningDummyTx = getBlockToMineTransaction(dummyTransaction); CMutableTransaction tx; - tx.vin.emplace_back( COutPoint(dummyTransaction.GetHash(), 0u) ); + tx.vin.emplace_back( COutPoint(OutputHash(dummyTransaction.GetHash()), 0u) ); tx.vout.push_back(CTxOut(100,managedScript)); manager->addTransaction(tx,nullptr, false); @@ -884,7 +884,7 @@ BOOST_AUTO_TEST_CASE(willNotAddDepositUnconfirmedTransactions) CBlock blockMiningDummyTx = getBlockToMineTransaction(dummyTransaction); CMutableTransaction tx; - tx.vin.emplace_back( COutPoint(dummyTransaction.GetHash(), 0u) ); + tx.vin.emplace_back( COutPoint(OutputHash(dummyTransaction.GetHash()), 0u) ); tx.vout.push_back(CTxOut(100,managedScript)); manager->addTransaction(tx,nullptr, true); @@ -904,7 +904,7 @@ BOOST_AUTO_TEST_CASE(willStopRecognizingUTXOsForWhichTheScriptHasBeenRevoked) CBlock blockMiningDummyTx = getBlockToMineTransaction(dummyTransaction); CMutableTransaction tx; - tx.vin.emplace_back( COutPoint(dummyTransaction.GetHash(), 0u) ); + tx.vin.emplace_back( COutPoint(OutputHash(dummyTransaction.GetHash()), 0u) ); tx.vout.push_back(CTxOut(100,managedScript)); CBlock block = getBlockToMineTransaction(tx); manager->addTransaction(tx,&block, true); @@ -932,7 +932,7 @@ BOOST_AUTO_TEST_CASE(willWriteTxToBackendOnAddition) CBlock blockMiningDummyTx = getBlockToMineTransaction(dummyTransaction); CMutableTransaction tx; - tx.vin.emplace_back( COutPoint(dummyTransaction.GetHash(), 0u) ); + tx.vin.emplace_back( COutPoint(OutputHash(dummyTransaction.GetHash()), 0u) ); tx.vout.push_back(CTxOut(100,managedScript)); CBlock block = getBlockToMineTransaction(tx); diff --git a/divi/src/test/coins_tests.cpp b/divi/src/test/coins_tests.cpp index a6cfd876c..df65c1895 100644 --- a/divi/src/test/coins_tests.cpp +++ b/divi/src/test/coins_tests.cpp @@ -19,10 +19,10 @@ namespace class CCoinsViewTest : public CCoinsView { uint256 hashBestBlock_; - std::map map_; + std::map map_; public: - bool GetCoins(const uint256& txid, CCoins& coins) const override + bool GetCoins(const OutputHash& txid, CCoins& coins) const override { auto it = map_.find(txid); if (it == map_.end()) { @@ -36,7 +36,7 @@ class CCoinsViewTest : public CCoinsView return true; } - bool HaveCoins(const uint256& txid) const override + bool HaveCoins(const OutputHash& txid) const override { CCoins coins; return GetCoins(txid, coins); @@ -88,7 +88,7 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test) bool missed_an_entry = false; // A simple map to track what we expect the cache stack to represent. - std::map result; + std::map result; // The cache stack. CCoinsViewTest base; // A CCoinsViewTest at the bottom. @@ -96,16 +96,16 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test) stack.push_back(new CCoinsViewCache(&base)); // Start with one cache. // Use a limited set of random transaction ids, so we do test overwriting entries. - std::vector txids; + std::vector txids; txids.resize(NUM_SIMULATION_ITERATIONS / 8); for (unsigned int i = 0; i < txids.size(); i++) { - txids[i] = GetRandHash(); + txids[i] = OutputHash(GetRandHash()); } for (unsigned int i = 0; i < NUM_SIMULATION_ITERATIONS; i++) { // Do a random modification. { - uint256 txid = txids[insecure_rand() % txids.size()]; // txid we're going to modify in this iteration. + OutputHash txid = txids[insecure_rand() % txids.size()]; // txid we're going to modify in this iteration. CCoins& coins = result[txid]; CCoinsModifier entry = stack.back()->ModifyCoins(txid); BOOST_CHECK(coins == *entry); diff --git a/divi/src/test/kernel_tests.cpp b/divi/src/test/kernel_tests.cpp index 46df29b32..59c700e0d 100644 --- a/divi/src/test/kernel_tests.cpp +++ b/divi/src/test/kernel_tests.cpp @@ -63,12 +63,12 @@ class CheckCoinstakeForVaultsTestFixture dummyTxNonVault.vout[i].scriptPubKey = scriptNonVault; } - coins.ModifyCoins(dummyTxVault.GetHash())->FromTx(dummyTxVault, 0); - coins.ModifyCoins(dummyTxNonVault.GetHash())->FromTx(dummyTxNonVault, 0); + coins.ModifyCoins(OutputHash(dummyTxVault.GetHash()))->FromTx(dummyTxVault, 0); + coins.ModifyCoins(OutputHash(dummyTxNonVault.GetHash()))->FromTx(dummyTxNonVault, 0); for (unsigned i = 0; i < dummyTxVault.vout.size(); ++i) { - vaultCoins.emplace_back(dummyTxVault.GetHash(), i); - nonVaultCoins.emplace_back(dummyTxNonVault.GetHash(), i); + vaultCoins.emplace_back(OutputHash(dummyTxVault.GetHash()), i); + nonVaultCoins.emplace_back(OutputHash(dummyTxNonVault.GetHash()), i); } } diff --git a/divi/src/test/mempool_tests.cpp b/divi/src/test/mempool_tests.cpp index 36ec62ebb..e4b3ee0a6 100644 --- a/divi/src/test/mempool_tests.cpp +++ b/divi/src/test/mempool_tests.cpp @@ -52,16 +52,16 @@ class MempoolTestFixture CMutableTransaction mtx; mtx.vout.emplace_back(2 * COIN, CScript () << OP_TRUE); mtx.vout.emplace_back(COIN, CScript () << OP_TRUE); - coins.ModifyCoins(mtx.GetHash())->FromTx(mtx, 0); + coins.ModifyCoins(OutputHash(mtx.GetHash()))->FromTx(mtx, 0); coins.SetBestBlock(fakeChain.activeChain->Tip()->GetBlockHash()); txParent.vin.resize(2); - txParent.vin[0].prevout = COutPoint(mtx.GetHash(), 0); + txParent.vin[0].prevout = COutPoint(OutputHash(mtx.GetHash()), 0); txParent.vin[0].scriptSig = CScript() << OP_11; /* Add a second input to make sure the transaction does not qualify as coinbase and thus has a bare txid unequal to its normal hash. */ - txParent.vin[1].prevout = COutPoint(mtx.GetHash(), 1); + txParent.vin[1].prevout = COutPoint(OutputHash(mtx.GetHash()), 1); txParent.vin[1].scriptSig = CScript() << OP_12; txParent.vout.resize(3); for (int i = 0; i < 3; i++) @@ -75,7 +75,7 @@ class MempoolTestFixture { txChild[i].vin.resize(1); txChild[i].vin[0].scriptSig = CScript() << OP_11; - txChild[i].vin[0].prevout.hash = txParent.GetHash(); + txChild[i].vin[0].prevout.hash = OutputHash(txParent.GetHash()); txChild[i].vin[0].prevout.n = i; txChild[i].vout.resize(1); txChild[i].vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; @@ -87,7 +87,7 @@ class MempoolTestFixture { txGrandChild[i].vin.resize(1); txGrandChild[i].vin[0].scriptSig = CScript() << OP_11; - txGrandChild[i].vin[0].prevout.hash = txChild[i].GetBareTxid(); + txGrandChild[i].vin[0].prevout.hash = OutputHash(txChild[i].GetBareTxid()); txGrandChild[i].vin[0].prevout.n = 0; txGrandChild[i].vout.resize(1); txGrandChild[i].vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; @@ -196,20 +196,20 @@ BOOST_AUTO_TEST_CASE(MempoolOutpointLookup) AddAll(); CCoinsViewMemPool viewPool(&coins, testPool); - BOOST_CHECK(testPool.lookupOutpoint(txParent.GetHash(), tx)); - BOOST_CHECK(!testPool.lookupOutpoint(txParent.GetBareTxid(), tx)); - BOOST_CHECK(!testPool.lookupOutpoint(txChild[0].GetHash(), tx)); - BOOST_CHECK(testPool.lookupOutpoint(txChild[0].GetBareTxid(), tx)); + BOOST_CHECK(testPool.lookupOutpoint(OutputHash(txParent.GetHash()), tx)); + BOOST_CHECK(!testPool.lookupOutpoint(OutputHash(txParent.GetBareTxid()), tx)); + BOOST_CHECK(!testPool.lookupOutpoint(OutputHash(txChild[0].GetHash()), tx)); + BOOST_CHECK(testPool.lookupOutpoint(OutputHash(txChild[0].GetBareTxid()), tx)); - BOOST_CHECK(viewPool.HaveCoins(txParent.GetHash())); - BOOST_CHECK(viewPool.GetCoins(txParent.GetHash(), c)); - BOOST_CHECK(!viewPool.HaveCoins(txParent.GetBareTxid())); - BOOST_CHECK(!viewPool.GetCoins(txParent.GetBareTxid(), c)); + BOOST_CHECK(viewPool.HaveCoins(OutputHash(txParent.GetHash()))); + BOOST_CHECK(viewPool.GetCoins(OutputHash(txParent.GetHash()), c)); + BOOST_CHECK(!viewPool.HaveCoins(OutputHash(txParent.GetBareTxid()))); + BOOST_CHECK(!viewPool.GetCoins(OutputHash(txParent.GetBareTxid()), c)); - BOOST_CHECK(!viewPool.HaveCoins(txChild[0].GetHash())); - BOOST_CHECK(!viewPool.GetCoins(txChild[0].GetHash(), c)); - BOOST_CHECK(viewPool.HaveCoins(txChild[0].GetBareTxid())); - BOOST_CHECK(viewPool.GetCoins(txChild[0].GetBareTxid(), c)); + BOOST_CHECK(!viewPool.HaveCoins(OutputHash(txChild[0].GetHash()))); + BOOST_CHECK(!viewPool.GetCoins(OutputHash(txChild[0].GetHash()), c)); + BOOST_CHECK(viewPool.HaveCoins(OutputHash(txChild[0].GetBareTxid()))); + BOOST_CHECK(viewPool.GetCoins(OutputHash(txChild[0].GetBareTxid()), c)); } BOOST_AUTO_TEST_CASE(MempoolExists) @@ -236,8 +236,8 @@ BOOST_AUTO_TEST_CASE(MempoolSpentIndex) testPool.addUnchecked(txParent.GetHash(), CTxMemPoolEntry(txParent, 0, 0, 0.0, 1), coins); testPool.addUnchecked(txChild[0].GetHash(), CTxMemPoolEntry(txChild[0], 0, 0, 0.0, 1), coins); - const CSpentIndexKey keyParent(txParent.GetHash(), 0); - const CSpentIndexKey keyChild(txChild[0].GetHash(), 0); + const CSpentIndexKey keyParent(OutputHash(txParent.GetHash()), 0); + const CSpentIndexKey keyChild(OutputHash(txChild[0].GetHash()), 0); CSpentIndexValue value; BOOST_CHECK(testPool.getSpentIndex(keyParent, value)); diff --git a/divi/src/test/multisig_tests.cpp b/divi/src/test/multisig_tests.cpp index c4a2b7d47..afce70119 100644 --- a/divi/src/test/multisig_tests.cpp +++ b/divi/src/test/multisig_tests.cpp @@ -79,7 +79,7 @@ BOOST_AUTO_TEST_CASE(multisig_verify) txTo[i].vin.resize(1); txTo[i].vout.resize(1); txTo[i].vin[0].prevout.n = i; - txTo[i].vin[0].prevout.hash = txFrom.GetHash(); + txTo[i].vin[0].prevout.hash = OutputHash(txFrom.GetHash()); txTo[i].vout[0].nValue = 1; } @@ -347,7 +347,7 @@ BOOST_AUTO_TEST_CASE(multisig_Sign) txTo[i].vin.resize(1); txTo[i].vout.resize(1); txTo[i].vin[0].prevout.n = i; - txTo[i].vin[0].prevout.hash = txFrom.GetHash(); + txTo[i].vin[0].prevout.hash = OutputHash(txFrom.GetHash()); txTo[i].vout[0].nValue = 1; } diff --git a/divi/src/test/script_CLTV_tests.cpp b/divi/src/test/script_CLTV_tests.cpp index a5e5ec257..3227704cf 100644 --- a/divi/src/test/script_CLTV_tests.cpp +++ b/divi/src/test/script_CLTV_tests.cpp @@ -30,8 +30,8 @@ class ScriptCLTVTestFixture ScriptCLTVTestFixture() { - tx.vin.emplace_back(uint256(), 0, CScript(), 1); - tx.vin.emplace_back(uint256(), 0, CScript(), 1); + tx.vin.emplace_back(OutputHash(uint256()), 0, CScript(), 1); + tx.vin.emplace_back(OutputHash(uint256()), 0, CScript(), 1); } /** Asserts that a given script is invalid with CLTV enabled in combination diff --git a/divi/src/test/script_P2SH_tests.cpp b/divi/src/test/script_P2SH_tests.cpp index 68a67290a..812b25ce0 100644 --- a/divi/src/test/script_P2SH_tests.cpp +++ b/divi/src/test/script_P2SH_tests.cpp @@ -45,7 +45,7 @@ Verify(const CScript& scriptSig, const CScript& scriptPubKey, bool fStrict, Scri txTo.vin.resize(1); txTo.vout.resize(1); txTo.vin[0].prevout.n = 0; - txTo.vin[0].prevout.hash = txFrom.GetHash(); + txTo.vin[0].prevout.hash = OutputHash(txFrom.GetHash()); txTo.vin[0].scriptSig = scriptSig; txTo.vout[0].nValue = 1; @@ -105,7 +105,7 @@ BOOST_AUTO_TEST_CASE(sign) txTo[i].vin.resize(1); txTo[i].vout.resize(1); txTo[i].vin[0].prevout.n = i; - txTo[i].vin[0].prevout.hash = txFrom.GetHash(); + txTo[i].vin[0].prevout.hash = OutputHash(txFrom.GetHash()); txTo[i].vout[0].nValue = 1; #ifdef ENABLE_WALLET BOOST_CHECK_MESSAGE(IsMine(keystore, txFrom.vout[i].scriptPubKey) != isminetype::ISMINE_NO, strprintf("IsMine %d", i)); @@ -207,7 +207,7 @@ BOOST_AUTO_TEST_CASE(set) txTo[i].vin.resize(1); txTo[i].vout.resize(1); txTo[i].vin[0].prevout.n = i; - txTo[i].vin[0].prevout.hash = txFrom.GetHash(); + txTo[i].vin[0].prevout.hash = OutputHash(txFrom.GetHash()); txTo[i].vout[0].nValue = 1*CENT; txTo[i].vout[0].scriptPubKey = inner[i]; #ifdef ENABLE_WALLET @@ -334,7 +334,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) txFrom.vout[6].scriptPubKey = GetScriptForDestination(CScriptID(twentySigops)); txFrom.vout[6].nValue = 6000; - coins.ModifyCoins(txFrom.GetHash())->FromTx(txFrom, 0); + coins.ModifyCoins(OutputHash(txFrom.GetHash()))->FromTx(txFrom, 0); CMutableTransaction txTo; txTo.vout.resize(1); @@ -344,7 +344,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) for (int i = 0; i < 5; i++) { txTo.vin[i].prevout.n = i; - txTo.vin[i].prevout.hash = txFrom.GetHash(); + txTo.vin[i].prevout.hash = OutputHash(txFrom.GetHash()); } BOOST_CHECK(SignSignature(keystore, txFrom, txTo, 0)); BOOST_CHECK(SignSignature(keystore, txFrom, txTo, 1)); @@ -375,7 +375,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) txToNonStd1.vout[0].nValue = 1000; txToNonStd1.vin.resize(1); txToNonStd1.vin[0].prevout.n = 5; - txToNonStd1.vin[0].prevout.hash = txFrom.GetHash(); + txToNonStd1.vin[0].prevout.hash = OutputHash(txFrom.GetHash()); txToNonStd1.vin[0].scriptSig << static_cast >(sixteenSigops); BOOST_CHECK(!::AreInputsStandard(txToNonStd1, coins)); @@ -387,7 +387,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) txToNonStd2.vout[0].nValue = 1000; txToNonStd2.vin.resize(1); txToNonStd2.vin[0].prevout.n = 6; - txToNonStd2.vin[0].prevout.hash = txFrom.GetHash(); + txToNonStd2.vin[0].prevout.hash = OutputHash(txFrom.GetHash()); txToNonStd2.vin[0].scriptSig << static_cast >(twentySigops); BOOST_CHECK(!::AreInputsStandard(txToNonStd2, coins)); diff --git a/divi/src/test/script_tests.cpp b/divi/src/test/script_tests.cpp index 02d3fc709..85de3698b 100644 --- a/divi/src/test/script_tests.cpp +++ b/divi/src/test/script_tests.cpp @@ -90,7 +90,7 @@ CMutableTransaction BuildSpendingTransaction(const CScript& scriptSig, const CMu txSpend.nLockTime = 0; txSpend.vin.resize(1); txSpend.vout.resize(1); - txSpend.vin[0].prevout.hash = txCredit.GetHash(); + txSpend.vin[0].prevout.hash = OutputHash(txCredit.GetHash()); txSpend.vin[0].prevout.n = 0; txSpend.vin[0].scriptSig = scriptSig; txSpend.vin[0].nSequence = std::numeric_limits::max(); diff --git a/divi/src/test/sighash_tests.cpp b/divi/src/test/sighash_tests.cpp index 3a00ffde2..b6d795b42 100644 --- a/divi/src/test/sighash_tests.cpp +++ b/divi/src/test/sighash_tests.cpp @@ -110,7 +110,7 @@ void static RandomTransaction(CMutableTransaction &tx, bool fSingle) { for (int in = 0; in < ins; in++) { tx.vin.push_back(CTxIn()); CTxIn &txin = tx.vin.back(); - txin.prevout.hash = GetRandHash(); + txin.prevout.hash = OutputHash(GetRandHash()); txin.prevout.n = insecure_rand() % 4; RandomScript(txin.scriptSig); txin.nSequence = (insecure_rand() % 2) ? insecure_rand() : (unsigned int)-1; diff --git a/divi/src/test/transaction_tests.cpp b/divi/src/test/transaction_tests.cpp index 81d3e7172..c305b6201 100644 --- a/divi/src/test/transaction_tests.cpp +++ b/divi/src/test/transaction_tests.cpp @@ -134,7 +134,7 @@ BOOST_AUTO_TEST_CASE(tx_valid) break; } - mapprevOutScriptPubKeys[COutPoint(uint256(vinput[0].get_str()), vinput[1].get_int())] = ParseScript(vinput[2].get_str()); + mapprevOutScriptPubKeys[COutPoint(OutputHash(uint256(vinput[0].get_str())), vinput[1].get_int())] = ParseScript(vinput[2].get_str()); } if (!fValid) { @@ -220,7 +220,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid) break; } - mapprevOutScriptPubKeys[COutPoint(uint256(vinput[0].get_str()), vinput[1].get_int())] = ParseScript(vinput[2].get_str()); + mapprevOutScriptPubKeys[COutPoint(OutputHash(uint256(vinput[0].get_str())), vinput[1].get_int())] = ParseScript(vinput[2].get_str()); } if (!fValid) @@ -298,14 +298,14 @@ SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet) dummyTransactions[0].vout[0].scriptPubKey << ToByteVector(key[0].GetPubKey()) << OP_CHECKSIG; dummyTransactions[0].vout[1].nValue = 50*CENT; dummyTransactions[0].vout[1].scriptPubKey << ToByteVector(key[1].GetPubKey()) << OP_CHECKSIG; - coinsRet.ModifyCoins(dummyTransactions[0].GetHash())->FromTx(dummyTransactions[0], 0); + coinsRet.ModifyCoins(OutputHash(dummyTransactions[0].GetHash()))->FromTx(dummyTransactions[0], 0); dummyTransactions[1].vout.resize(2); dummyTransactions[1].vout[0].nValue = 21*CENT; dummyTransactions[1].vout[0].scriptPubKey = GetScriptForDestination(key[2].GetPubKey().GetID()); dummyTransactions[1].vout[1].nValue = 22*CENT; dummyTransactions[1].vout[1].scriptPubKey = GetScriptForDestination(key[3].GetPubKey().GetID()); - coinsRet.ModifyCoins(dummyTransactions[1].GetHash())->FromTx(dummyTransactions[1], 0); + coinsRet.ModifyCoins(OutputHash(dummyTransactions[1].GetHash()))->FromTx(dummyTransactions[1], 0); return dummyTransactions; } @@ -318,13 +318,13 @@ BOOST_AUTO_TEST_CASE(test_Get) CMutableTransaction t1; t1.vin.resize(3); - t1.vin[0].prevout.hash = dummyTransactions[0].GetHash(); + t1.vin[0].prevout.hash = OutputHash(dummyTransactions[0].GetHash()); t1.vin[0].prevout.n = 1; t1.vin[0].scriptSig << std::vector(65, 0); - t1.vin[1].prevout.hash = dummyTransactions[1].GetHash(); + t1.vin[1].prevout.hash = OutputHash(dummyTransactions[1].GetHash()); t1.vin[1].prevout.n = 0; t1.vin[1].scriptSig << std::vector(65, 0) << std::vector(33, 4); - t1.vin[2].prevout.hash = dummyTransactions[1].GetHash(); + t1.vin[2].prevout.hash = OutputHash(dummyTransactions[1].GetHash()); t1.vin[2].prevout.n = 1; t1.vin[2].scriptSig << std::vector(65, 0) << std::vector(33, 4); t1.vout.resize(2); @@ -352,7 +352,7 @@ BOOST_AUTO_TEST_CASE(test_IsStandard) CMutableTransaction t; t.vin.resize(1); - t.vin[0].prevout.hash = dummyTransactions[0].GetHash(); + t.vin[0].prevout.hash = OutputHash(dummyTransactions[0].GetHash()); t.vin[0].prevout.n = 1; t.vin[0].scriptSig << std::vector(65, 0); t.vout.resize(1); diff --git a/divi/src/test/wallet_coinmanagement_tests.cpp b/divi/src/test/wallet_coinmanagement_tests.cpp index 0de8121ed..8347f2ea0 100644 --- a/divi/src/test/wallet_coinmanagement_tests.cpp +++ b/divi/src/test/wallet_coinmanagement_tests.cpp @@ -78,7 +78,7 @@ BOOST_AUTO_TEST_CASE(willNotAllowSpendingLockedCoin) unsigned outputIndex=0; const CWalletTx& wtx = wallet.AddDefaultTx(defaultScript,outputIndex); - wallet.LockCoin(COutPoint(wtx.GetHash(),outputIndex)); + wallet.LockCoin(COutPoint(OutputHash(wtx.GetHash()), outputIndex)); bool fIsSpendable = false; BOOST_CHECK(!wallet.IsAvailableForSpending(&wtx,outputIndex,false,fIsSpendable)); @@ -132,8 +132,8 @@ BOOST_AUTO_TEST_CASE(willAllowSpendingLockedCoinAfterUnlock) unsigned outputIndex=0; const CWalletTx& wtx = wallet.AddDefaultTx(defaultScript,outputIndex); - wallet.LockCoin(COutPoint(wtx.GetHash(),outputIndex)); - wallet.UnlockCoin(COutPoint(wtx.GetHash(),outputIndex)); + wallet.LockCoin(COutPoint(OutputHash(wtx.GetHash()), outputIndex)); + wallet.UnlockCoin(COutPoint(OutputHash(wtx.GetHash()), outputIndex)); bool fIsSpendable = false; BOOST_CHECK(wallet.IsAvailableForSpending(&wtx,outputIndex,false,fIsSpendable)); @@ -381,7 +381,7 @@ BOOST_AUTO_TEST_CASE(willEnsureLockedCoinsDoNotCountTowardStakableBalance) wallet.FakeAddToChain(firstNormalTx); wallet.FakeAddToChain(secondNormalTx); - wallet.LockCoin(COutPoint(firstNormalTx.GetHash(),firstTxOutputIndex)); + wallet.LockCoin(COutPoint(OutputHash(firstNormalTx.GetHash()), firstTxOutputIndex)); BOOST_CHECK_EQUAL_MESSAGE(wallet.GetBalance(), firstNormalTxValue+secondNormalTxValue,"Total balance was not the expected amount"); BOOST_CHECK_EQUAL_MESSAGE(wallet.GetStakingBalance(), secondNormalTxValue,"Staking balance was not the expected amount"); @@ -403,8 +403,8 @@ BOOST_AUTO_TEST_CASE(willEnsureStakingBalanceAndTotalBalanceAgreeEvenIfTxsBelong wallet.FakeAddToChain(secondNormalTx); std::set stakableCoins; - stakableCoins.insert(StakableCoin(firstNormalTx,COutPoint(firstNormalTx.GetHash(),firstTxOutputIndex),firstNormalTx.hashBlock)); - stakableCoins.insert(StakableCoin(secondNormalTx,COutPoint(secondNormalTx.GetHash(),secondTxOutputIndex),secondNormalTx.hashBlock)); + stakableCoins.insert(StakableCoin(firstNormalTx, COutPoint(OutputHash(firstNormalTx.GetHash()), firstTxOutputIndex), firstNormalTx.hashBlock)); + stakableCoins.insert(StakableCoin(secondNormalTx, COutPoint(OutputHash(secondNormalTx.GetHash()), secondTxOutputIndex), secondNormalTx.hashBlock)); BOOST_CHECK_EQUAL_MESSAGE(stakableCoins.size(),2,"Missing coins in the stakable set"); } From 6a232b75639b8c8f74ca16cebf6975051b890084 Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Thu, 9 Sep 2021 13:04:03 +0200 Subject: [PATCH 11/14] Consensus and mempool parts of segwit-light. This implements "segwit-light" in the node (consensus and mempool): After activation of a new fork, outputs created by new transactions are referred to by the creating transaction's bare txid rather than the normal hash (txid). This makes chains of constructed transactions immune to transaction malleability. In the mempool, we use the current chain tip to determine the activation state of the fork, as we can't know for sure when a transaction will be confirmed (and thus what activation state will be in effect then). This will lead to issues right around the fork activation time, but we will simply disallow spending of unconfirmed outputs in the mempool and wallet "around" the fork time temporarily to resolve this. The wallet is also not yet updated to take the change into account when selecting coins. --- divi/src/ActiveChainManager.cpp | 4 +++- divi/src/ForkActivation.cpp | 5 ++++- divi/src/ForkActivation.h | 7 +++++++ divi/src/UtxoCheckingAndUpdating.cpp | 3 +++ divi/src/UtxoCheckingAndUpdating.h | 10 ++++++++++ divi/src/init.cpp | 2 +- divi/src/main.cpp | 7 +++++-- divi/src/rpcblockchain.cpp | 2 +- divi/src/test/mempool_tests.cpp | 2 +- divi/src/txmempool.cpp | 23 ++++++++++++++++++++--- divi/src/txmempool.h | 2 +- 11 files changed, 56 insertions(+), 11 deletions(-) diff --git a/divi/src/ActiveChainManager.cpp b/divi/src/ActiveChainManager.cpp index 74bf7e0e8..da7cb9ae4 100644 --- a/divi/src/ActiveChainManager.cpp +++ b/divi/src/ActiveChainManager.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -87,7 +88,8 @@ bool ActiveChainManager::DisconnectBlock( return error("DisconnectBlock() : block and undo data inconsistent"); } - const BlockUtxoHasher utxoHasher; + const ActivationState as(pindex); + const BlockUtxoHasher utxoHasher(as); bool fClean = true; IndexDatabaseUpdates indexDBUpdates; diff --git a/divi/src/ForkActivation.cpp b/divi/src/ForkActivation.cpp index 66f868f75..c3a664e6c 100644 --- a/divi/src/ForkActivation.cpp +++ b/divi/src/ForkActivation.cpp @@ -28,7 +28,10 @@ const std::unordered_map> ACTIVATION_TIMES = { {Fork::UniformLotteryWinners, unixTimestampForDec31stMidnight}, /* FIXME: Schedule for a real time. */ {Fork::CheckLockTimeVerify, 2000000000}, - {Fork::DeprecateMasternodes,2000000000} + {Fork::DeprecateMasternodes,2000000000}, + /* FIXME: Set real activation time for segwit light. It is after + staking vaults. */ + {Fork::SegwitLight, 2100000000}, }; const std::unordered_set> REQUIRE_BLOCK_INDEX_CONTEXT = { diff --git a/divi/src/ForkActivation.h b/divi/src/ForkActivation.h index d1617055c..4be1897b5 100644 --- a/divi/src/ForkActivation.h +++ b/divi/src/ForkActivation.h @@ -24,6 +24,13 @@ enum Fork UniformLotteryWinners, CheckLockTimeVerify, DeprecateMasternodes, + + /** + * Start of "segwit light": The UTXOs created by transactions from after + * the fork will be indexed by a "bare txid", which does not include + * any signature data. This fixes transaction malleability. + */ + SegwitLight, }; /** diff --git a/divi/src/UtxoCheckingAndUpdating.cpp b/divi/src/UtxoCheckingAndUpdating.cpp index 031fdd3dc..eee6ee3c0 100644 --- a/divi/src/UtxoCheckingAndUpdating.cpp +++ b/divi/src/UtxoCheckingAndUpdating.cpp @@ -1,5 +1,6 @@ #include +#include #include #include #include @@ -14,6 +15,8 @@ OutputHash BlockUtxoHasher::GetUtxoHash(const CTransaction& tx) const { + if (as.IsActive(Fork::SegwitLight)) + return OutputHash(tx.GetBareTxid()); return OutputHash(tx.GetHash()); } diff --git a/divi/src/UtxoCheckingAndUpdating.h b/divi/src/UtxoCheckingAndUpdating.h index 5230070b5..c0fa57dcb 100644 --- a/divi/src/UtxoCheckingAndUpdating.h +++ b/divi/src/UtxoCheckingAndUpdating.h @@ -7,6 +7,7 @@ #include #include +class ActivationState; class BlockMap; class CTransaction; class CValidationState; @@ -56,8 +57,17 @@ class TransactionUtxoHasher class BlockUtxoHasher : public TransactionUtxoHasher { +private: + + /** The activation state used for checking on segwit light. */ + const ActivationState& as; + public: + explicit BlockUtxoHasher(const ActivationState& a) + : as(a) + {} + OutputHash GetUtxoHash(const CTransaction& tx) const override; }; diff --git a/divi/src/init.cpp b/divi/src/init.cpp index dcc9528d1..d8bf9b09b 100644 --- a/divi/src/init.cpp +++ b/divi/src/init.cpp @@ -116,7 +116,7 @@ CClientUIInterface uiInterface; bool fAddressIndex = false; bool fSpentIndex = false; const FeeAndPriorityCalculator& feeAndPriorityCalculator = FeeAndPriorityCalculator::instance(); -CTxMemPool mempool(feeAndPriorityCalculator.getMinimumRelayFeeRate(), fAddressIndex, fSpentIndex); +CTxMemPool mempool(chainActive, feeAndPriorityCalculator.getMinimumRelayFeeRate(), fAddressIndex, fSpentIndex); bool static InitError(const std::string& str) { diff --git a/divi/src/main.cpp b/divi/src/main.cpp index b8b453f98..d7de99c59 100755 --- a/divi/src/main.cpp +++ b/divi/src/main.cpp @@ -22,6 +22,7 @@ #include "coins.h" #include #include "FeeRate.h" +#include "ForkActivation.h" #include "init.h" #include "kernel.h" #include "masternode-payments.h" @@ -1011,7 +1012,8 @@ bool ConnectBlock( LogWalletBalance(); static const CChainParams& chainParameters = Params(); - const BlockUtxoHasher utxoHasher; + const ActivationState as(pindex); + const BlockUtxoHasher utxoHasher(as); VerifyBestBlockIsAtPreviousBlock(pindex,view); if (block.GetHash() == Params().HashGenesisBlock()) @@ -2240,7 +2242,8 @@ bool static LoadBlockIndexDB(string& strError) 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"; return false; } - const BlockUtxoHasher utxoHasher; + const ActivationState as(pindex); + const BlockUtxoHasher utxoHasher(as); std::vector vtxundo; vtxundo.reserve(block.vtx.size() - 1); diff --git a/divi/src/rpcblockchain.cpp b/divi/src/rpcblockchain.cpp index 3bfa94d41..3c8f64949 100644 --- a/divi/src/rpcblockchain.cpp +++ b/divi/src/rpcblockchain.cpp @@ -417,7 +417,7 @@ Value gettxout(const Array& params, bool fHelp) "gettxout \"txid\" n ( includemempool )\n" "\nReturns details about an unspent transaction output.\n" "\nArguments:\n" - "1. \"txid\" (string, required) The transaction id\n" + "1. \"txid\" (string, required) The transaction id or bare txid (after segwit-light)\n" "2. n (numeric, required) vout value\n" "3. includemempool (boolean, optional) Whether to included the mem pool\n" "\nResult:\n" diff --git a/divi/src/test/mempool_tests.cpp b/divi/src/test/mempool_tests.cpp index e4b3ee0a6..00768d206 100644 --- a/divi/src/test/mempool_tests.cpp +++ b/divi/src/test/mempool_tests.cpp @@ -44,7 +44,7 @@ class MempoolTestFixture MempoolTestFixture() : fakeChain(1, 1500000000, 1), - testPool(CFeeRate(0), addressIndex, spentIndex), + testPool(*fakeChain.activeChain, CFeeRate(0), addressIndex, spentIndex), coinsMemPool(nullptr, testPool), coins(&coinsMemPool) { std::unique_ptr utxoHasher(new MockUtxoHasher()); diff --git a/divi/src/txmempool.cpp b/divi/src/txmempool.cpp index 7b8af075a..92f001a48 100644 --- a/divi/src/txmempool.cpp +++ b/divi/src/txmempool.cpp @@ -7,6 +7,7 @@ #include "txmempool.h" #include "clientversion.h" +#include "ForkActivation.h" #include "main.h" #include "streams.h" #include "Logging.h" @@ -32,14 +33,30 @@ bool IsMemPoolHeight(unsigned coinHeight) namespace { -/** The UTXO hasher used in mempool logic. */ +/** The UTXO hasher used in mempool logic. It uses the state of segwit-light + * based on the latest chain tip (since we can't know when a particular + * transaction will be confirmed / what the real activation state will be then). + * Spending of unconfirmed change around the fork will be discouraged by + * policy, so that this should not be an issue in practice. */ class MempoolUtxoHasher : public TransactionUtxoHasher { +private: + + /** The active chain for getting the current chain tip. */ + const CChain& activeChain; + public: + explicit MempoolUtxoHasher (const CChain& ca) + : activeChain(ca) + {} + OutputHash GetUtxoHash(const CTransaction& tx) const override { + const ActivationState as(activeChain.Tip()); + if (as.IsActive(Fork::SegwitLight)) + return OutputHash(tx.GetBareTxid()); return OutputHash(tx.GetHash()); } @@ -47,7 +64,7 @@ class MempoolUtxoHasher : public TransactionUtxoHasher } // anonymous namespace -CTxMemPool::CTxMemPool(const CFeeRate& _minRelayFee, +CTxMemPool::CTxMemPool(const CChain& activeChain, const CFeeRate& _minRelayFee, const bool& addressIndex, const bool& spentIndex ): fSanityCheck(false) , nTransactionsUpdated(0) @@ -68,7 +85,7 @@ CTxMemPool::CTxMemPool(const CFeeRate& _minRelayFee, // than an hour or three to confirm are highly variable. // feePolicyEstimator = new CfeePolicyEstimator(25); - utxoHasher.reset(new MempoolUtxoHasher()); + utxoHasher.reset(new MempoolUtxoHasher(activeChain)); } CTxMemPool::~CTxMemPool() diff --git a/divi/src/txmempool.h b/divi/src/txmempool.h index 6438d243e..34f1ca6c8 100644 --- a/divi/src/txmempool.h +++ b/divi/src/txmempool.h @@ -105,7 +105,7 @@ class CTxMemPool std::map mapTx; std::map mapNextTx; - explicit CTxMemPool(const CFeeRate& _minRelayFee, + explicit CTxMemPool(const CChain& activeChain, const CFeeRate& _minRelayFee, const bool& addressIndex, const bool& spentIndex); ~CTxMemPool(); From 44445686dc1b4a3c80f538fdfb7c6ba3e9db778e Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Tue, 26 Jan 2021 16:15:43 +0100 Subject: [PATCH 12/14] Segwit light for spending coins. Handle segwit-light properly when spending coins, either for staking or normal transactions in the wallet. Depending on when a transaction was confirmed, we need to make sure to include the right outpoint (with txid or bare txid) in the transaction spending an output. This is done through a custom UTXO hasher used in the wallet, which specifically works for CWalletTx. --- divi/src/primitives/transaction.h | 2 ++ divi/src/wallet.cpp | 36 ++++++++++++++++++++++++++----- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/divi/src/primitives/transaction.h b/divi/src/primitives/transaction.h index 5d0e5095a..649460aec 100644 --- a/divi/src/primitives/transaction.h +++ b/divi/src/primitives/transaction.h @@ -139,6 +139,8 @@ class CTransaction CTransaction& operator=(const CTransaction& tx); + virtual ~CTransaction() = default; + ADD_SERIALIZE_METHODS; template diff --git a/divi/src/wallet.cpp b/divi/src/wallet.cpp index 3d2018676..dfcea231a 100644 --- a/divi/src/wallet.cpp +++ b/divi/src/wallet.cpp @@ -13,6 +13,7 @@ #include "checkpoints.h" #include #include +#include "ForkActivation.h" #include "net.h" #include "script/script.h" #include "script/sign.h" @@ -176,17 +177,42 @@ std::set AddressBookManager::GetAccountAddresses(std::string str namespace { -/** Dummy UTXO hasher for the wallet. For now, this just always returns - * the normal txid, but we will later change it to return the proper hash - * for a WalletTx. */ +/** UTXO hasher for wallet transactions. It uses the transaction's known block + * hash (from CMerkleTx) to determine the activation state of segwit light. */ class WalletUtxoHasher : public TransactionUtxoHasher { +private: + + const I_MerkleTxConfirmationNumberCalculator& confirmationNumberCalculator_; + const CChain& chainActive_; + public: + WalletUtxoHasher(const I_MerkleTxConfirmationNumberCalculator& calc, const CChain& chain) + : confirmationNumberCalculator_(calc), chainActive_(chain) + {} + OutputHash GetUtxoHash(const CTransaction& tx) const override { - return OutputHash(tx.GetHash()); + const CMerkleTx* mtx = dynamic_cast(&tx); + assert(mtx != nullptr); + + const auto conf = confirmationNumberCalculator_.FindConfirmedBlockIndexAndDepth(*mtx); + const CBlockIndex* pindexBlock = conf.first; + + /* If the transaction is not yet confirmed in a block, we use the current + tip to determine the segwit-light activation status. This is not + perfect around the activation time, but there is nothing we can do + in that case anyway. Mempool and wallet discourage spending unconfirmed + outputs around the segwit-light fork anyway. */ + if (conf.second <= 0) + pindexBlock = chainActive_.Tip(); + + assert(pindexBlock != nullptr); + const ActivationState as(pindexBlock); + + return OutputHash(as.IsActive(Fork::SegwitLight) ? mtx->GetBareTxid() : mtx->GetHash()); } }; @@ -211,7 +237,7 @@ CWallet::CWallet(const CChain& chain, const BlockMap& blockMap , transactionRecord_(new WalletTransactionRecord(cs_wallet,strWalletFile) ) , outputTracker_( new SpentOutputTracker(*transactionRecord_,*confirmationNumberCalculator_) ) , pwalletdbEncryption() - , utxoHasher(new WalletUtxoHasher() ) + , utxoHasher(new WalletUtxoHasher(*confirmationNumberCalculator_, chain) ) , nWalletVersion(FEATURE_BASE) , nWalletMaxVersion(FEATURE_BASE) , mapKeyMetadata() From 2badd7017ebb350b7301e22c232e917f55a7686a Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Fri, 15 Jan 2021 15:59:32 +0100 Subject: [PATCH 13/14] Restrict unconfirmed change around fork. Around the segwit-light fork (some hours before and after), we should avoid spending unconfirmed outputs: If we do that, it is unclear whether or not the fork will be active in the block that confirms those spends, and thus we can't reliably construct a valid chain. With this change, we introduce a (temporary) measure to disallow those spends in the wallet and mempool policy in a window of time around the planned fork activation. --- divi/qa/rpc-tests/around_segwit_light.py | 137 +++++++++++++++++++++++ divi/qa/rpc-tests/test_runner.py | 1 + divi/src/ForkActivation.cpp | 9 ++ divi/src/ForkActivation.h | 9 ++ divi/src/main.cpp | 25 ++++- divi/src/wallet.cpp | 15 ++- divi/src/wallet.h | 5 + 7 files changed, 199 insertions(+), 2 deletions(-) create mode 100755 divi/qa/rpc-tests/around_segwit_light.py diff --git a/divi/qa/rpc-tests/around_segwit_light.py b/divi/qa/rpc-tests/around_segwit_light.py new file mode 100755 index 000000000..eceeddb0f --- /dev/null +++ b/divi/qa/rpc-tests/around_segwit_light.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python3 +# Copyright (c) 2020 The DIVI developers +# Distributed under the MIT/X11 software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# Tests the policy changes in wallet and mempool around the +# segwit-light activation time that prevent spending of +# unconfirmed outputs. + +from test_framework import BitcoinTestFramework +from authproxy import JSONRPCException +from util import * + +ACTIVATION_TIME = 2_100_000_000 + +# Offset around the activation time where no restrictions are in place. +NO_RESTRICTIONS = 24 * 3_600 + +# Offset around the activation time where the wallet disallows +# spending unconfirmed change, but the mempool still accepts it. +WALLET_RESTRICTED = 10 * 3_600 + +# Offset around the activation time where wallet and mempool +# disallow spending unconfirmed outputs. +MEMPOOL_RESTRICTED = 3_600 + + +class AroundSegwitLightTest (BitcoinTestFramework): + + def setup_network (self, split=False): + args = ["-debug", "-spendzeroconfchange"] + self.nodes = start_nodes (1, self.options.tmpdir, extra_args=[args]) + self.node = self.nodes[0] + self.is_network_split = False + + def build_spending_chain (self, key, addr, initialValue): + """ + Spends the initialValue to addr, and then builds a follow-up + transaction that spends that output again back to addr with + a smaller value (for fees). The second transaction is built + using the raw-transactions API and returned as hex, not + submitted to the node already. + """ + + txid = self.node.sendtoaddress (addr, initialValue) + data = self.node.getrawtransaction (txid, 1) + outputIndex = None + for i in range (len (data["vout"])): + if data["vout"][i]["value"] == initialValue: + outputIndex = i + break + assert outputIndex is not None + + inputs = [{"txid": data[key], "vout": outputIndex}] + outputs = {addr: initialValue - 10} + tx = self.node.createrawtransaction (inputs, outputs) + signed = self.node.signrawtransaction (tx) + assert signed["complete"] + + return signed["hex"] + + def expect_unrestricted (self): + """ + Checks that spending of unconfirmed change is possible without + restrictions of wallet or mempool. + """ + + balance = self.node.getbalance () + addr = self.node.getnewaddress () + + self.node.sendtoaddress (addr, balance - 10) + self.node.sendtoaddress (addr, balance - 20) + self.node.setgenerate (True, 1) + + def expect_wallet_restricted (self, key): + """ + Checks that the wallet forbids spending unconfirmed change, + while the mempool still allows it. + """ + + balance = self.node.getbalance () + addr = self.node.getnewaddress () + + self.node.sendtoaddress (addr, balance - 10) + assert_raises (JSONRPCException, self.node.sendtoaddress, addr, balance - 20) + self.node.setgenerate (True, 1) + + balance = self.node.getbalance () + tx = self.build_spending_chain (key, addr, balance - 10) + self.node.sendrawtransaction (tx) + self.node.setgenerate (True, 1) + + def expect_mempool_restricted (self, key): + """ + Checks that the mempool does not allow spending unconfirmed + outputs (even if the transaction is built and submitted directly), + while blocks should still allow it. + """ + + balance = self.node.getbalance () + addr = self.node.getnewaddress () + + tx = self.build_spending_chain (key, addr, balance - 10) + assert_raises (JSONRPCException, self.node.sendrawtransaction, tx) + self.node.generateblock ({"extratx": [tx]}) + + def run_test (self): + self.node.setgenerate (True, 30) + + # Before restrictions come into force, doing a normal + # spend of unconfirmed change through the wallet is fine. + set_node_times (self.nodes, ACTIVATION_TIME - NO_RESTRICTIONS) + self.expect_unrestricted () + + # Next the wallet doesn't allow those spends, but the mempool + # will (if done directly). + set_node_times (self.nodes, ACTIVATION_TIME - WALLET_RESTRICTED) + self.expect_wallet_restricted ("txid") + + # Very close to the fork (on both sides), even the mempool won't + # allow spending unconfirmed change. If we include it directly in + # a block, it works. + set_node_times (self.nodes, ACTIVATION_TIME - MEMPOOL_RESTRICTED) + self.expect_mempool_restricted ("txid") + set_node_times (self.nodes, ACTIVATION_TIME + MEMPOOL_RESTRICTED) + self.node.setgenerate (True, 1) + self.expect_mempool_restricted ("baretxid") + + # Finally, we should run into mempool-only or no restrictions + # at all if we go further into the future, away from the fork. + set_node_times (self.nodes, ACTIVATION_TIME + WALLET_RESTRICTED) + self.expect_wallet_restricted ("baretxid") + set_node_times (self.nodes, ACTIVATION_TIME + NO_RESTRICTIONS) + self.expect_unrestricted () + +if __name__ == '__main__': + AroundSegwitLightTest ().main () diff --git a/divi/qa/rpc-tests/test_runner.py b/divi/qa/rpc-tests/test_runner.py index 45aeec670..512c94c5e 100755 --- a/divi/qa/rpc-tests/test_runner.py +++ b/divi/qa/rpc-tests/test_runner.py @@ -83,6 +83,7 @@ 'StakingVaultDeactivation.py', 'StakingVaultSpam.py', 'StakingVaultRedundancy.py', + 'around_segwit_light.py', 'forknotify.py', 'getchaintips.py', 'httpbasics.py', diff --git a/divi/src/ForkActivation.cpp b/divi/src/ForkActivation.cpp index c3a664e6c..de47879bf 100644 --- a/divi/src/ForkActivation.cpp +++ b/divi/src/ForkActivation.cpp @@ -6,7 +6,9 @@ #include "chain.h" #include "primitives/block.h" +#include "timedata.h" +#include #include #include @@ -59,3 +61,10 @@ bool ActivationState::IsActive(const Fork f) const assert(mit != ACTIVATION_TIMES.end()); return currentTime >= mit->second; } + +bool ActivationState::CloseToSegwitLight(const int maxSeconds) +{ + const int64_t now = GetAdjustedTime(); + const int64_t activation = ACTIVATION_TIMES.at(Fork::SegwitLight); + return std::abs(now - activation) <= maxSeconds; +} diff --git a/divi/src/ForkActivation.h b/divi/src/ForkActivation.h index 4be1897b5..ca6c250cf 100644 --- a/divi/src/ForkActivation.h +++ b/divi/src/ForkActivation.h @@ -62,6 +62,15 @@ class ActivationState */ bool IsActive(Fork f) const; + /** + * Returns true if the current time is "close" to the activation of + * segwit-light (before or after), with close being within the given + * number of seconds. This is used for a temporary measure to disallow + * (by mempool and wallet policy) spending of unconfirmed change + * around the fork. + */ + static bool CloseToSegwitLight(int maxSeconds); + }; #endif // FORK_ACTIVATION_H diff --git a/divi/src/main.cpp b/divi/src/main.cpp index d7de99c59..08e6deb53 100755 --- a/divi/src/main.cpp +++ b/divi/src/main.cpp @@ -66,6 +66,7 @@ #include #include #include +#include #include #include #include @@ -114,6 +115,13 @@ extern bool fAddressIndex; extern bool fSpentIndex; extern CTxMemPool mempool; +/** Number of seconds around the "segwit light" fork for which we disallow + * spending unconfirmed outputs (to avoid messing up with the change + * itself). This should be smaller than the matching constant for the + * wallet (so the wallet does not construct things the mempool won't + * accept in the end). */ +constexpr int SEGWIT_LIGHT_FORBID_SPENDING_ZERO_CONF_SECONDS = 14400; + bool IsFinalTx(const CTransaction& tx, const CChain& activeChain, int nBlockHeight = 0 , int64_t nBlockTime = 0); CCheckpointServices checkpointsVerifier(GetCurrentChainCheckpoints); @@ -620,7 +628,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState& state, const CTransa // do all inputs exist? // Note that this does not check for the presence of actual outputs (see the next check for that), // only helps filling in pfMissingInputs (to determine missing vs spent). - for (const CTxIn txin : tx.vin) { + for (const auto& txin : tx.vin) { if (!view.HaveCoins(txin.prevout.hash)) { if (pfMissingInputs) *pfMissingInputs = true; @@ -634,6 +642,21 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState& state, const CTransa return state.Invalid(error("%s : inputs already spent",__func__), REJECT_DUPLICATE, "bad-txns-inputs-spent"); + // If we are close to the segwit-light activation (before or after), + // do not allow spending of zero-confirmation outputs. This would + // mess up with the fork, as it is not clear whether the fork will be + // active or not when they get confirmed (and thus it is not possible + // to reliably construct a chain of transactions). + if (ActivationState::CloseToSegwitLight(SEGWIT_LIGHT_FORBID_SPENDING_ZERO_CONF_SECONDS)) { + for (const auto& txin : tx.vin) { + const auto* coins = view.AccessCoins(txin.prevout.hash); + assert(coins != nullptr && coins->IsAvailable(txin.prevout.n)); + if (coins->nHeight == CTxMemPoolEntry::MEMPOOL_HEIGHT) + return state.DoS(0, error("%s : spending zero-confirmation output around segwit light", __func__), + REJECT_NONSTANDARD, "zero-conf-segwit-light"); + } + } + // Bring the best block into scope view.GetBestBlock(); diff --git a/divi/src/wallet.cpp b/divi/src/wallet.cpp index dfcea231a..0502f88e5 100644 --- a/divi/src/wallet.cpp +++ b/divi/src/wallet.cpp @@ -45,6 +45,13 @@ extern Settings& settings; const FeeAndPriorityCalculator& priorityFeeCalculator = FeeAndPriorityCalculator::instance(); +/** Number of seconds around the "segwit light" fork for which we force + * not spending unconfirmed change (to avoid messing up with the change + * itself). This should be larger than the matching constant for the + * mempool (so the wallet does not construct things the mempool won't + * accept in the end). */ +constexpr int SEGWIT_LIGHT_DISABLE_SPENDING_ZERO_CONF_SECONDS = 43200; + extern CCriticalSection cs_main; extern CTxMemPool mempool; @@ -2045,6 +2052,12 @@ bool CWallet::SelectCoinsMinConf( return true; } +bool CWallet::AllowSpendingZeroConfirmationChange() const +{ + return allowSpendingZeroConfirmationOutputs + && !ActivationState::CloseToSegwitLight(SEGWIT_LIGHT_DISABLE_SPENDING_ZERO_CONF_SECONDS); +} + void AppendOutputs( const std::vector >& intendedDestinations, CMutableTransaction& txNew) @@ -2813,7 +2826,7 @@ bool CWallet::IsTrusted(const CWalletTx& walletTransaction) const return true; if (nDepth < 0) return false; - if (!allowSpendingZeroConfirmationOutputs || !DebitsFunds(walletTransaction, isminetype::ISMINE_SPENDABLE)) // using wtx's cached debit + if (!AllowSpendingZeroConfirmationChange() || !DebitsFunds(walletTransaction, isminetype::ISMINE_SPENDABLE)) // using wtx's cached debit return false; // Trusted if all inputs are from us and are in the mempool: diff --git a/divi/src/wallet.h b/divi/src/wallet.h index 0ca4e602e..d7ab5beae 100644 --- a/divi/src/wallet.h +++ b/divi/src/wallet.h @@ -200,6 +200,11 @@ class CWallet : isminetype IsMine(const CTxIn& txin) const; bool IsMine(const CTransaction& tx) const; + /** Returns true if the wallet should allow spending of unconfirmed change. + * This is mostly determined by allowSpendingZeroConfirmationOutputs, + * but is forced to off around the segwit-light fork. */ + bool AllowSpendingZeroConfirmationChange() const; + protected: // CWalletDB: load from disk methods void LoadWalletTransaction(const CWalletTx& wtxIn) override; From e1cd852eba064defbe1fd9bb01e766e4b629b8e9 Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Thu, 10 Dec 2020 14:57:58 +0100 Subject: [PATCH 14/14] Regtest for segwit light. This adds the new regtest segwit_light.py, which verifies that sending of transactions and staking still works after the fork, and that the new rules work as expected. It also explicitly checks that transaction malleability is not an issue anymore for chains of pre-signed transactions. --- divi/qa/rpc-tests/around_segwit_light.py | 2 +- divi/qa/rpc-tests/segwit_light.py | 116 +++++++++++++++++++++++ divi/qa/rpc-tests/test_runner.py | 1 + 3 files changed, 118 insertions(+), 1 deletion(-) create mode 100755 divi/qa/rpc-tests/segwit_light.py diff --git a/divi/qa/rpc-tests/around_segwit_light.py b/divi/qa/rpc-tests/around_segwit_light.py index eceeddb0f..f42f902ad 100755 --- a/divi/qa/rpc-tests/around_segwit_light.py +++ b/divi/qa/rpc-tests/around_segwit_light.py @@ -11,7 +11,7 @@ from authproxy import JSONRPCException from util import * -ACTIVATION_TIME = 2_100_000_000 +from segwit_light import ACTIVATION_TIME # Offset around the activation time where no restrictions are in place. NO_RESTRICTIONS = 24 * 3_600 diff --git a/divi/qa/rpc-tests/segwit_light.py b/divi/qa/rpc-tests/segwit_light.py new file mode 100755 index 000000000..7244324d0 --- /dev/null +++ b/divi/qa/rpc-tests/segwit_light.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 +# Copyright (c) 2020 The DIVI developers +# Distributed under the MIT/X11 software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# Tests the policy changes in wallet and mempool around the +# segwit-light activation time that prevent spending of +# unconfirmed outputs. + +from test_framework import BitcoinTestFramework +from util import * + +from PowToPosTransition import createPoSStacks, generatePoSBlocks + +ACTIVATION_TIME = 2_100_000_000 + + +class SegwitLightTest (BitcoinTestFramework): + + def setup_network (self, split=False): + args = ["-debug", "-spendzeroconfchange"] + self.nodes = start_nodes (2, self.options.tmpdir, extra_args=[args]*2) + connect_nodes (self.nodes[0], 1) + self.is_network_split = False + + def run_test (self): + # Activate the fork and PoS. We go beyond the fork to ensure + # the mempool/wallet limitations are lifted already. + set_node_times (self.nodes, ACTIVATION_TIME + 3_600 * 24 * 7) + reconnect_all (self.nodes) + self.nodes[0].setgenerate (True, 1) + sync_blocks (self.nodes) + createPoSStacks (self.nodes[:1], self.nodes) + generatePoSBlocks (self.nodes, 0, 100) + blk = self.nodes[1].getblockheader (self.nodes[1].getbestblockhash ()) + assert_greater_than (blk["time"], ACTIVATION_TIME) + + # Send some normal transactions from the wallet (but in a chain). + self.nodes[0].sendtoaddress (self.nodes[1].getnewaddress (), 1_000) + generatePoSBlocks (self.nodes, 0, 1) + assert_equal (self.nodes[1].getbalance (), 1_000) + addr = self.nodes[0].getnewaddress () + id1 = self.nodes[1].sendtoaddress (addr, 900) + id2 = self.nodes[1].sendtoaddress (addr, 90) + id3 = self.nodes[1].sendtoaddress (addr, 9) + assert_equal (set (self.nodes[1].getrawmempool ()), set ([id1, id2, id3])) + sync_mempools (self.nodes) + generatePoSBlocks (self.nodes, 0, 1) + assert_equal (self.nodes[1].getrawmempool (), []) + assert_greater_than (1, self.nodes[1].getbalance ()) + + # Build a transaction on top of an unconfirmed one, that we will malleate. + # The prepared transaction should still be valid. For malleating, we use + # funds on a 1-of-2 multisig address, and then change which wallet + # is signing. + keys = [ + n.validateaddress (n.getnewaddress ())["pubkey"] + for n in self.nodes + ] + multisig = self.nodes[0].addmultisigaddress (1, keys) + assert_equal (self.nodes[1].addmultisigaddress (1, keys), multisig) + txid0 = self.nodes[0].sendtoaddress (multisig, 1_000) + data0 = self.nodes[0].getrawtransaction (txid0, 1) + btxid = data0["baretxid"] + outputIndex = None + for i in range (len (data0["vout"])): + if data0["vout"][i]["scriptPubKey"]["addresses"] == [multisig]: + assert outputIndex is None + outputIndex = i + assert outputIndex is not None + generatePoSBlocks (self.nodes, 0, 1) + out = self.nodes[0].gettxout (btxid, outputIndex) + assert_equal (out["confirmations"], 1) + assert_equal (out["value"], 1_000) + assert_equal (out["scriptPubKey"]["addresses"], [multisig]) + + inputs = [{"txid": btxid, "vout": outputIndex}] + tempAddr = self.nodes[0].getnewaddress ("temp") + outputs = {tempAddr: 999} + unsigned1 = self.nodes[0].createrawtransaction (inputs, outputs) + signed1 = self.nodes[0].signrawtransaction (unsigned1) + assert_equal (signed1["complete"], True) + signed1 = signed1["hex"] + data1 = self.nodes[0].decoderawtransaction (signed1) + + prevtx = [ + { + "txid": data1["baretxid"], + "vout": 0, + "scriptPubKey": self.nodes[0].validateaddress (tempAddr)["scriptPubKey"], + } + ] + inputs = [{"txid": data1["baretxid"], "vout": 0}] + finalAddr = self.nodes[1].getnewaddress ("final") + outputs = {finalAddr: 998} + unsigned2 = self.nodes[0].createrawtransaction (inputs, outputs) + signed2 = self.nodes[0].signrawtransaction (unsigned2, prevtx) + assert_equal (signed2["complete"], True) + signed2 = signed2["hex"] + data2 = self.nodes[0].decoderawtransaction (signed2) + + signed1p = self.nodes[1].signrawtransaction (unsigned1) + assert_equal (signed1p["complete"], True) + signed1p = signed1p["hex"] + data1p = self.nodes[0].decoderawtransaction (signed1p) + assert_equal (data1["baretxid"], data1p["baretxid"]) + assert data1["txid"] != data1p["txid"] + + self.nodes[0].sendrawtransaction (signed1p) + self.nodes[0].sendrawtransaction (signed2) + generatePoSBlocks (self.nodes, 0, 1) + sync_blocks (self.nodes) + assert_equal (self.nodes[1].getbalance ("final"), 998) + +if __name__ == '__main__': + SegwitLightTest ().main () diff --git a/divi/qa/rpc-tests/test_runner.py b/divi/qa/rpc-tests/test_runner.py index 512c94c5e..04b185acf 100755 --- a/divi/qa/rpc-tests/test_runner.py +++ b/divi/qa/rpc-tests/test_runner.py @@ -114,6 +114,7 @@ 'rpcbind_test.py', 'remotestart.py', 'remotestart.py --outdated_ping', + 'segwit_light.py', 'smartfees.py', 'sync.py', 'txindex.py',