mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-05-29 23:33:33 +02:00
Add a "tx output spender" index
Adds an outpoint -> txid index, which can be used to find which transactions spent a given output. We use a composite key with 2 parts (suggested by @romanz): hash(spent outpoint) and tx position, with an empty value. To find the spending tx for a given outpoint, we do a prefix search (prefix being the hash of the provided outpoint), and for all keys that match this prefix we load the tx at the position specified in the key and return it, along with the block hash, if does spend the provided outpoint. To handle reorgs we just erase the keys computed from the removed block. This index is extremely useful for Lightning and more generally for layer-2 protocols that rely on chains of unpublished transactions. If enabled, this index will be used by `gettxspendingprevout` when it does not find a spending transaction in the mempool.
This commit is contained in:
@@ -115,6 +115,7 @@ add_executable(test_bitcoin
|
||||
txdownload_tests.cpp
|
||||
txgraph_tests.cpp
|
||||
txindex_tests.cpp
|
||||
txospenderindex_tests.cpp
|
||||
txpackage_tests.cpp
|
||||
txreconciliation_tests.cpp
|
||||
txrequest_tests.cpp
|
||||
|
||||
77
src/test/txospenderindex_tests.cpp
Normal file
77
src/test/txospenderindex_tests.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
// Copyright (c) The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <chainparams.h>
|
||||
#include <index/txospenderindex.h>
|
||||
#include <test/util/setup_common.h>
|
||||
#include <util/time.h>
|
||||
#include <validation.h>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(txospenderindex_tests)
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(txospenderindex_initial_sync, TestChain100Setup)
|
||||
{
|
||||
TxoSpenderIndex txospenderindex(interfaces::MakeChain(m_node), 1 << 20, true);
|
||||
BOOST_REQUIRE(txospenderindex.Init());
|
||||
|
||||
// Mine blocks for coinbase maturity, so we can spend some coinbase outputs in the test.
|
||||
for (int i = 0; i < 50; i++) {
|
||||
std::vector<CMutableTransaction> no_txns;
|
||||
CreateAndProcessBlock(no_txns, this->m_coinbase_txns[i]->vout[0].scriptPubKey);
|
||||
}
|
||||
std::vector<COutPoint> spent(10);
|
||||
std::vector<CMutableTransaction> spender(spent.size());
|
||||
|
||||
for (size_t i = 0; i < spent.size(); i++) {
|
||||
spent[i] = COutPoint(this->m_coinbase_txns[i]->GetHash(), 0);
|
||||
spender[i].version = 1;
|
||||
spender[i].vin.resize(1);
|
||||
spender[i].vin[0].prevout.hash = spent[i].hash;
|
||||
spender[i].vin[0].prevout.n = spent[i].n;
|
||||
spender[i].vout.resize(1);
|
||||
spender[i].vout[0].nValue = this->m_coinbase_txns[i]->GetValueOut();
|
||||
spender[i].vout[0].scriptPubKey = this->m_coinbase_txns[i]->vout[0].scriptPubKey;
|
||||
|
||||
// Sign:
|
||||
std::vector<unsigned char> vchSig;
|
||||
const uint256 hash = SignatureHash(this->m_coinbase_txns[i]->vout[0].scriptPubKey, spender[i], 0, SIGHASH_ALL, 0, SigVersion::BASE);
|
||||
coinbaseKey.Sign(hash, vchSig);
|
||||
vchSig.push_back((unsigned char)SIGHASH_ALL);
|
||||
spender[i].vin[0].scriptSig << vchSig;
|
||||
}
|
||||
|
||||
CBlock block = CreateAndProcessBlock(spender, this->m_coinbase_txns[0]->vout[0].scriptPubKey);
|
||||
|
||||
// Transaction should not be found in the index before it is started.
|
||||
for (const auto& outpoint : spent) {
|
||||
BOOST_CHECK(!txospenderindex.FindSpender(outpoint).value());
|
||||
}
|
||||
|
||||
// BlockUntilSyncedToCurrentChain should return false before txospenderindex is started.
|
||||
BOOST_CHECK(!txospenderindex.BlockUntilSyncedToCurrentChain());
|
||||
|
||||
txospenderindex.Sync();
|
||||
for (size_t i = 0; i < spent.size(); i++) {
|
||||
const auto tx_spender{txospenderindex.FindSpender(spent[i])};
|
||||
BOOST_REQUIRE(tx_spender.has_value());
|
||||
BOOST_REQUIRE(tx_spender->has_value());
|
||||
BOOST_CHECK_EQUAL((*tx_spender)->tx->GetHash(), spender[i].GetHash());
|
||||
BOOST_CHECK_EQUAL((*tx_spender)->block_hash, block.GetHash());
|
||||
}
|
||||
|
||||
// It is not safe to stop and destroy the index until it finishes handling
|
||||
// the last BlockConnected notification. The BlockUntilSyncedToCurrentChain()
|
||||
// call above is sufficient to ensure this, but the
|
||||
// SyncWithValidationInterfaceQueue() call below is also needed to ensure
|
||||
// TSAN always sees the test thread waiting for the notification thread, and
|
||||
// avoid potential false positive reports.
|
||||
m_node.validation_signals->SyncWithValidationInterfaceQueue();
|
||||
|
||||
// shutdown sequence (c.f. Shutdown() in init.cpp)
|
||||
txospenderindex.Stop();
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
Reference in New Issue
Block a user