diff --git a/chronik/chronik-bridge/src/ffi.rs b/chronik/chronik-bridge/src/ffi.rs --- a/chronik/chronik-bridge/src/ffi.rs +++ b/chronik/chronik-bridge/src/ffi.rs @@ -73,6 +73,10 @@ #[namespace = ""] type CBlock; + /// ::Config from config.h + #[namespace = ""] + type Config; + /// Bridge to bitcoind to access the node type ChronikBridge; @@ -94,7 +98,10 @@ ); /// Make the bridge given the NodeContext - fn make_bridge(node: &NodeContext) -> UniquePtr<ChronikBridge>; + fn make_bridge( + config: &Config, + node: &NodeContext, + ) -> UniquePtr<ChronikBridge>; /// Return the tip of the chain of the node. /// Returns hash=000...000, height=-1 if there's no block on the chain. @@ -107,6 +114,12 @@ hash: [u8; 32], ) -> Result<&CBlockIndex>; + /// Load the CBlock data of this CBlockIndex from the disk + fn load_block( + self: &ChronikBridge, + block_index: &CBlockIndex, + ) -> Result<UniquePtr<CBlock>>; + /// Find at which block the given block_index forks off from the node. fn find_fork( self: &ChronikBridge, diff --git a/chronik/chronik-cpp/chronik_bridge.h b/chronik/chronik-cpp/chronik_bridge.h --- a/chronik/chronik-cpp/chronik_bridge.h +++ b/chronik/chronik-cpp/chronik_bridge.h @@ -10,6 +10,7 @@ class CBlock; class CBlockIndex; +class Config; namespace node { struct NodeContext; @@ -39,19 +40,24 @@ * Bridge to bitcoind to access the node. */ class ChronikBridge { + const Config &m_config; const node::NodeContext &m_node; public: - ChronikBridge(const node::NodeContext &node) : m_node(node) {} + ChronikBridge(const Config &config, const node::NodeContext &node) + : m_config(config), m_node(node) {} const CBlockIndex &get_chain_tip() const; const CBlockIndex &lookup_block_index(std::array<uint8_t, 32> hash) const; + std::unique_ptr<CBlock> load_block(const CBlockIndex &bindex) const; + const CBlockIndex &find_fork(const CBlockIndex &index) const; }; -std::unique_ptr<ChronikBridge> make_bridge(const node::NodeContext &node); +std::unique_ptr<ChronikBridge> make_bridge(const Config &config, + const node::NodeContext &node); Block bridge_block(const CBlock &block, const CBlockIndex &bindex); diff --git a/chronik/chronik-cpp/chronik_bridge.cpp b/chronik/chronik-cpp/chronik_bridge.cpp --- a/chronik/chronik-cpp/chronik_bridge.cpp +++ b/chronik/chronik-cpp/chronik_bridge.cpp @@ -3,10 +3,13 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <blockindex.h> +#include <chainparams.h> #include <chronik-bridge/src/ffi.rs.h> #include <chronik-cpp/chronik_bridge.h> #include <chronik-cpp/util/hash.h> +#include <config.h> #include <logging.h> +#include <node/blockstorage.h> #include <node/context.h> #include <node/ui_interface.h> #include <shutdown.h> @@ -49,6 +52,16 @@ return *pindex; } +std::unique_ptr<CBlock> +ChronikBridge::load_block(const CBlockIndex &bindex) const { + CBlock block; + if (!node::ReadBlockFromDisk(block, &bindex, + m_config.GetChainParams().GetConsensus())) { + throw std::runtime_error("Reading block data failed"); + } + return std::make_unique<CBlock>(std::move(block)); +} + const CBlockIndex &ChronikBridge::find_fork(const CBlockIndex &index) const { const CBlockIndex *fork = WITH_LOCK( cs_main, @@ -59,8 +72,9 @@ return *fork; } -std::unique_ptr<ChronikBridge> make_bridge(const node::NodeContext &node) { - return std::make_unique<ChronikBridge>(node); +std::unique_ptr<ChronikBridge> make_bridge(const Config &config, + const node::NodeContext &node) { + return std::make_unique<ChronikBridge>(config, node); } chronik_bridge::Block bridge_block(const CBlock &block, diff --git a/chronik/test/chronikbridge_tests.cpp b/chronik/test/chronikbridge_tests.cpp --- a/chronik/test/chronikbridge_tests.cpp +++ b/chronik/test/chronikbridge_tests.cpp @@ -7,6 +7,8 @@ #include <chronik-cpp/chronik_bridge.h> #include <chronik-cpp/util/hash.h> #include <config.h> +#include <streams.h> +#include <util/strencodings.h> #include <validation.h> #include <test/util/setup_common.h> @@ -23,33 +25,34 @@ } // Chain has no blocks yet: // get_chain_tip throws block_index_not_found - const chronik_bridge::ChronikBridge bridge(m_node); + const chronik_bridge::ChronikBridge bridge(GetConfig(), m_node); BOOST_CHECK_THROW(bridge.get_chain_tip(), chronik_bridge::block_index_not_found); } BOOST_FIXTURE_TEST_CASE(test_get_chain_tip_genesis, TestingSetup) { - const chronik_bridge::ChronikBridge bridge(m_node); + const Config &config = GetConfig(); + const chronik_bridge::ChronikBridge bridge(config, m_node); // Check for genesis block const CBlockIndex &bindex = bridge.get_chain_tip(); BOOST_CHECK_EQUAL(bindex.GetBlockHash(), - GetConfig().GetChainParams().GenesisBlock().GetHash()); + config.GetChainParams().GenesisBlock().GetHash()); } BOOST_FIXTURE_TEST_CASE(test_get_chain_tip_100, TestChain100Setup) { // Generate new block (at height 101) CBlock tip_block = CreateAndProcessBlock( {}, CScript() << std::vector<uint8_t>(33) << OP_CHECKSIG); - const chronik_bridge::ChronikBridge bridge(m_node); + const chronik_bridge::ChronikBridge bridge(GetConfig(), m_node); // Check if block is 101th const CBlockIndex &bindex = bridge.get_chain_tip(); BOOST_CHECK_EQUAL(bindex.GetBlockHash(), tip_block.GetHash()); } BOOST_FIXTURE_TEST_CASE(test_lookup_block_index, TestChain100Setup) { - const chronik_bridge::ChronikBridge bridge(m_node); - BlockHash genesis_hash = - GetConfig().GetChainParams().GenesisBlock().GetHash(); + const Config &config = GetConfig(); + const chronik_bridge::ChronikBridge bridge(config, m_node); + BlockHash genesis_hash = config.GetChainParams().GenesisBlock().GetHash(); const CBlockIndex &bindex_genesis = bridge.lookup_block_index(chronik::util::HashToArray(genesis_hash)); BOOST_CHECK_EQUAL(bindex_genesis.GetBlockHash(), genesis_hash); @@ -92,6 +95,23 @@ chainman.ActiveTip()->GetAncestor(49)->GetBlockHash()); } +BOOST_FIXTURE_TEST_CASE(test_load_block, TestChain100Setup) { + const Config &config = GetConfig(); + const chronik_bridge::ChronikBridge bridge(config, m_node); + ChainstateManager &chainman = *Assert(m_node.chainman); + const CBlockIndex &tip = *chainman.ActiveTip(); + + BOOST_CHECK_EQUAL(bridge.load_block(tip)->GetHash(), tip.GetBlockHash()); + + { + CDataStream expected(SER_NETWORK, PROTOCOL_VERSION); + CDataStream actual(SER_NETWORK, PROTOCOL_VERSION); + expected << config.GetChainParams().GenesisBlock(); + actual << *bridge.load_block(*tip.GetAncestor(0)); + BOOST_CHECK_EQUAL(HexStr(actual), HexStr(expected)); + } +} + BOOST_FIXTURE_TEST_CASE(test_get_block_ancestor, TestChain100Setup) { ChainstateManager &chainman = *Assert(m_node.chainman); const CBlockIndex &tip = *chainman.ActiveTip(); @@ -120,13 +140,14 @@ } BOOST_FIXTURE_TEST_CASE(test_get_block_info, TestChain100Setup) { - const chronik_bridge::ChronikBridge bridge(m_node); + const Config &config = GetConfig(); + const chronik_bridge::ChronikBridge bridge(config, m_node); ChainstateManager &chainman = *Assert(m_node.chainman); const CBlockIndex &tip = *chainman.ActiveTip(); chronik_bridge::BlockInfo expected_genesis_info{ .hash = chronik::util::HashToArray( - GetConfig().GetChainParams().GenesisBlock().GetHash()), + config.GetChainParams().GenesisBlock().GetHash()), .height = 0}; BOOST_CHECK(chronik_bridge::get_block_info(*tip.GetAncestor(0)) == expected_genesis_info);