mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-04-29 16:29:31 +02:00
Merge bitcoin/bitcoin#33657: rest: allow reading partial block data from storage
07135290c1rest: allow reading partial block data from storage (Roman Zeyde)4e2af1c065blockstorage: allow reading partial block data from storage (Roman Zeyde)f2fd1aa21cblockstorage: return an error code from `ReadRawBlock()` (Roman Zeyde) Pull request description: It allows fetching specific transactions using an external index, following https://github.com/bitcoin/bitcoin/pull/32541#issuecomment-3267485313. Currently, electrs and other indexers map between an address/scripthash to the list of the relevant transactions. However, in order to fetch those transactions from bitcoind, electrs relies on reading the whole block and post-filtering for a specific transaction[^1]. Other indexers use a `txindex` to fetch a transaction using its txid [^2][^3][^4]. The above approach has significant storage and CPU overhead, since the `txid` is a pseudo-random 32-byte value. Also, mainnet `txindex` takes ~60GB today. This PR is adding support for using the transaction's position within its block to be able to fetch it directly using [REST API](https://github.com/bitcoin/bitcoin/blob/master/doc/REST-interface.md), using the following HTTP request: ``` GET /rest/blockpart/BLOCKHASH.bin?offset=OFFSET&size=SIZE ``` - The offsets' index can be encoded much more efficiently ([~1.3GB today](https://github.com/romanz/bindex-rs/pull/66#issuecomment-3508476436)). - Address history query performance can be tested on mainnet using [1BitcoinEaterAddressDontSendf59kuE](https://mempool.space/address/1BitcoinEaterAddressDontSendf59kuE) - assuming warm OS block cache, [it takes <1s to fetch 5200 txs, i.e. <0.2ms per tx](https://github.com/romanz/bindex-rs/pull/66#issuecomment-3508476436) with [bindex](https://github.com/romanz/bindex-rs). - Only binary and hex response formats are supported. [^1]: https://github.com/romanz/electrs/blob/master/doc/schema.md [^2]: https://github.com/Blockstream/electrs/blob/new-index/doc/schema.md#txstore [^3]: https://github.com/spesmilo/electrumx/blob/master/docs/HOWTO.rst#prerequisites [^4]: https://github.com/cculianu/Fulcrum/blob/master/README.md#requirements ACKs for top commit: maflcko: review ACK07135290c1🏪 l0rinc: ACK07135290c1hodlinator: re-ACK07135290c1Tree-SHA512: bcce7bf4b9a3e5e920ab5a83e656f50d5d7840cdde6b7147d329cf578f8a2db555fc1aa5334e8ee64d5630d25839ece77a2cf421c6c3ac1fa379bb453163bd4f
This commit is contained in:
@@ -138,6 +138,68 @@ BOOST_FIXTURE_TEST_CASE(blockmanager_block_data_availability, TestChain100Setup)
|
||||
BOOST_CHECK(!blockman.CheckBlockDataAvailability(tip, *last_pruned_block));
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(blockmanager_block_data_part, TestChain100Setup)
|
||||
{
|
||||
LOCK(::cs_main);
|
||||
auto& chainman{m_node.chainman};
|
||||
auto& blockman{chainman->m_blockman};
|
||||
const CBlockIndex& tip{*chainman->ActiveTip()};
|
||||
const FlatFilePos tip_block_pos{tip.GetBlockPos()};
|
||||
|
||||
auto block{blockman.ReadRawBlock(tip_block_pos)};
|
||||
BOOST_REQUIRE(block);
|
||||
BOOST_REQUIRE_GE(block->size(), 200);
|
||||
|
||||
const auto expect_part{[&](size_t offset, size_t size) {
|
||||
auto res{blockman.ReadRawBlock(tip_block_pos, std::pair{offset, size})};
|
||||
BOOST_CHECK(res);
|
||||
const auto& part{res.value()};
|
||||
BOOST_CHECK_EQUAL_COLLECTIONS(part.begin(), part.end(), block->begin() + offset, block->begin() + offset + size);
|
||||
}};
|
||||
|
||||
expect_part(0, 20);
|
||||
expect_part(0, block->size() - 1);
|
||||
expect_part(0, block->size() - 10);
|
||||
expect_part(0, block->size());
|
||||
expect_part(1, block->size() - 1);
|
||||
expect_part(10, 20);
|
||||
expect_part(block->size() - 1, 1);
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(blockmanager_block_data_part_error, TestChain100Setup)
|
||||
{
|
||||
LOCK(::cs_main);
|
||||
auto& chainman{m_node.chainman};
|
||||
auto& blockman{chainman->m_blockman};
|
||||
const CBlockIndex& tip{*chainman->ActiveTip()};
|
||||
const FlatFilePos tip_block_pos{tip.GetBlockPos()};
|
||||
|
||||
auto block{blockman.ReadRawBlock(tip_block_pos)};
|
||||
BOOST_REQUIRE(block);
|
||||
BOOST_REQUIRE_GE(block->size(), 200);
|
||||
|
||||
const auto expect_part_error{[&](size_t offset, size_t size) {
|
||||
auto res{blockman.ReadRawBlock(tip_block_pos, std::pair{offset, size})};
|
||||
BOOST_CHECK(!res);
|
||||
BOOST_CHECK_EQUAL(res.error(), node::ReadRawError::BadPartRange);
|
||||
}};
|
||||
|
||||
expect_part_error(0, 0);
|
||||
expect_part_error(0, block->size() + 1);
|
||||
expect_part_error(0, std::numeric_limits<size_t>::max());
|
||||
expect_part_error(1, block->size());
|
||||
expect_part_error(2, block->size() - 1);
|
||||
expect_part_error(block->size() - 1, 2);
|
||||
expect_part_error(block->size() - 2, 3);
|
||||
expect_part_error(block->size() + 1, 0);
|
||||
expect_part_error(block->size() + 1, 1);
|
||||
expect_part_error(block->size() + 2, 2);
|
||||
expect_part_error(block->size(), 0);
|
||||
expect_part_error(block->size(), 1);
|
||||
expect_part_error(std::numeric_limits<size_t>::max(), 1);
|
||||
expect_part_error(std::numeric_limits<size_t>::max(), std::numeric_limits<size_t>::max());
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(blockmanager_readblock_hash_mismatch, TestingSetup)
|
||||
{
|
||||
CBlockIndex index;
|
||||
|
||||
Reference in New Issue
Block a user