kernel: Add function to read block undo data from disk to C header
What changed, and why it matters
This commit adds a new public programming interface (API) to the Bitcoin Core 'kernel' library that lets external callers read 'undo' data—renamed here as 'spent outputs'—from disk. Undo data records which previous coins were consumed by transactions in a block. It is normally used internally during chain reorganizations, but the change exposes it for external indexing and silent-payment scanning. The change is a feature addition, not a fix for a known vulnerability, and the commit message does not describe it as security-relevant.
Treat as a routine feature/API expansion. Reviewers should verify that the new opaque handles and copy/destroy functions maintain correct ownership semantics, that assert-based bounds failures are acceptable for a library API, and that exposing undo data does not violate any intended isolation model for the kernel library. No immediate security patch is indicated.
Security signals we found
New public C API surface increases attack/exposure area for library consumers
Exposes previously internal undo/rev data to external callers, raising potential information-disclosure considerations if access controls are weak
Uses assert() for bounds checking in index accessors, which will abort the process on out-of-range access rather than returning an error
No input sanitization beyond existing ReadBlockUndo; relies on caller-supplied block tree entry being valid
No security-relevance claimed by commit message or diff
Evidence from the diff
The patch extends src/kernel/bitcoinkernel.h, bitcoinkernel.cpp, and bitcoinkernel_wrapper.h with opaque C handles and accessor functions for CBlockUndo/CTxUndo/Coin objects: btck_block_spent_outputs_read, _copy, _count, _get_transaction_spent_outputs_at, _destroy; btck_transaction_spent_outputs_copy, _count, _get_coin_at, _destroy; and btck_coin_copy, _confirmation_height, _is_coinbase, _get_output, _destroy. It also adds C++ wrapper classes (BlockSpentOutputs, TransactionSpentOutputs, Coin) and unit tests in src/test/kernel/test_kernel.cpp. The read path uses the existing BlockManager::ReadBlockUndo. No validation logic, consensus code, or network behavior is changed.
Changed components
src/kernel/bitcoinkernel.hsrc/kernel/bitcoinkernel.cppsrc/kernel/bitcoinkernel_wrapper.hsrc/test/kernel/test_kernel.cppBitcoin Core kernel library public APIInspect captured patch +420 / −4
diff --git a/src/kernel/bitcoinkernel.cpp b/src/kernel/bitcoinkernel.cpp
index 21193ce4..b9c0acc8 100644
--- a/src/kernel/bitcoinkernel.cpp
+++ b/src/kernel/bitcoinkernel.cpp
@@ -7,6 +7,7 @@
#include <kernel/bitcoinkernel.h>
#include <chain.h>
+#include <coins.h>
#include <consensus/amount.h>
#include <consensus/validation.h>
#include <kernel/caches.h>
@@ -28,6 +29,7 @@
#include <sync.h>
#include <tinyformat.h>
#include <uint256.h>
+#include <undo.h>
#include <util/fs.h>
#include <util/result.h>
#include <util/signalinterrupt.h>
@@ -485,6 +487,9 @@ struct btck_ChainParameters : Handle<btck_ChainParameters, CChainParams> {};
struct btck_ChainstateManagerOptions : Handle<btck_ChainstateManagerOptions, ChainstateManagerOptions> {};
struct btck_ChainstateManager : Handle<btck_ChainstateManager, ChainMan> {};
struct btck_Chain : Handle<btck_Chain, CChain> {};
+struct btck_BlockSpentOutputs : Handle<btck_BlockSpentOutputs, std::shared_ptr<CBlockUndo>> {};
+struct btck_TransactionSpentOutputs : Handle<btck_TransactionSpentOutputs, CTxUndo> {};
+struct btck_Coin : Handle<btck_Coin, Coin> {};
btck_Transaction* btck_transaction_create(const void* raw_transaction, size_t raw_transaction_len)
{
@@ -1004,6 +1009,89 @@ btck_Block* btck_block_read(const btck_ChainstateManager* chainman, const btck_B
return btck_Block::create(block);
}
+btck_BlockSpentOutputs* btck_block_spent_outputs_read(const btck_ChainstateManager* chainman, const btck_BlockTreeEntry* entry)
+{
+ auto block_undo{std::make_shared<CBlockUndo>()};
+ if (btck_BlockTreeEntry::get(entry).nHeight < 1) {
+ LogDebug(BCLog::KERNEL, "The genesis block does not have any spent outputs.");
+ return btck_BlockSpentOutputs::create(block_undo);
+ }
+ if (!btck_ChainstateManager::get(chainman).m_chainman->m_blockman.ReadBlockUndo(*block_undo, btck_BlockTreeEntry::get(entry))) {
+ LogError("Failed to read block spent outputs data.");
+ return nullptr;
+ }
+ return btck_BlockSpentOutputs::create(block_undo);
+}
+
+btck_BlockSpentOutputs* btck_block_spent_outputs_copy(const btck_BlockSpentOutputs* block_spent_outputs)
+{
+ return btck_BlockSpentOutputs::copy(block_spent_outputs);
+}
+
+size_t btck_block_spent_outputs_count(const btck_BlockSpentOutputs* block_spent_outputs)
+{
+ return btck_BlockSpentOutputs::get(block_spent_outputs)->vtxundo.size();
+}
+
+const btck_TransactionSpentOutputs* btck_block_spent_outputs_get_transaction_spent_outputs_at(const btck_BlockSpentOutputs* block_spent_outputs, size_t transaction_index)
+{
+ assert(transaction_index < btck_BlockSpentOutputs::get(block_spent_outputs)->vtxundo.size());
+ const auto* tx_undo{&btck_BlockSpentOutputs::get(block_spent_outputs)->vtxundo.at(transaction_index)};
+ return btck_TransactionSpentOutputs::ref(tx_undo);
+}
+
+void btck_block_spent_outputs_destroy(btck_BlockSpentOutputs* block_spent_outputs)
+{
+ delete block_spent_outputs;
+}
+
+btck_TransactionSpentOutputs* btck_transaction_spent_outputs_copy(const btck_TransactionSpentOutputs* transaction_spent_outputs)
+{
+ return btck_TransactionSpentOutputs::copy(transaction_spent_outputs);
+}
+
+size_t btck_transaction_spent_outputs_count(const btck_TransactionSpentOutputs* transaction_spent_outputs)
+{
+ return btck_TransactionSpentOutputs::get(transaction_spent_outputs).vprevout.size();
+}
+
+void btck_transaction_spent_outputs_destroy(btck_TransactionSpentOutputs* transaction_spent_outputs)
+{
+ delete transaction_spent_outputs;
+}
+
+const btck_Coin* btck_transaction_spent_outputs_get_coin_at(const btck_TransactionSpentOutputs* transaction_spent_outputs, size_t coin_index)
+{
+ assert(coin_index < btck_TransactionSpentOutputs::get(transaction_spent_outputs).vprevout.size());
+ const Coin* coin{&btck_TransactionSpentOutputs::get(transaction_spent_outputs).vprevout.at(coin_index)};
+ return btck_Coin::ref(coin);
+}
+
+btck_Coin* btck_coin_copy(const btck_Coin* coin)
+{
+ return btck_Coin::copy(coin);
+}
+
+uint32_t btck_coin_confirmation_height(const btck_Coin* coin)
+{
+ return btck_Coin::get(coin).nHeight;
+}
+
+int btck_coin_is_coinbase(const btck_Coin* coin)
+{
+ return btck_Coin::get(coin).IsCoinBase() ? 1 : 0;
+}
+
+const btck_TransactionOutput* btck_coin_get_output(const btck_Coin* coin)
+{
+ return btck_TransactionOutput::ref(&btck_Coin::get(coin).out);
+}
+
+void btck_coin_destroy(btck_Coin* coin)
+{
+ delete coin;
+}
+
int btck_chainstate_manager_process_block(
btck_ChainstateManager* chainman,
const btck_Block* block,
diff --git a/src/kernel/bitcoinkernel.h b/src/kernel/bitcoinkernel.h
index e8694514..b6cfb0db 100644
--- a/src/kernel/bitcoinkernel.h
+++ b/src/kernel/bitcoinkernel.h
@@ -212,6 +212,36 @@ typedef struct btck_BlockValidationState btck_BlockValidationState;
*/
typedef struct btck_Chain btck_Chain;
+/**
+ * Opaque data structure for holding a block's spent outputs.
+ *
+ * Contains all the previous outputs consumed by all transactions in a specific
+ * block. Internally it holds a nested vector. The top level vector has an
+ * entry for each transaction in a block (in order of the actual transactions
+ * of the block and without the coinbase transaction). This is exposed through
+ * @ref btck_TransactionSpentOutputs. Each btck_TransactionSpentOutputs is in
+ * turn a vector of all the previous outputs of a transaction (in order of
+ * their corresponding inputs).
+ */
+typedef struct btck_BlockSpentOutputs btck_BlockSpentOutputs;
+
+/**
+ * Opaque data structure for holding a transaction's spent outputs.
+ *
+ * Holds the coins consumed by a certain transaction. Retrieved through the
+ * @ref btck_BlockSpentOutputs. The coins are in the same order as the
+ * transaction's inputs consuming them.
+ */
+typedef struct btck_TransactionSpentOutputs btck_TransactionSpentOutputs;
+
+/**
+ * Opaque data structure for holding a coin.
+ *
+ * Holds information on the @ref btck_TransactionOutput held within,
+ * including the height it was spent at and whether it is a coinbase output.
+ */
+typedef struct btck_Coin btck_Coin;
+
/** Current sync state passed to tip changed callbacks. */
typedef uint8_t btck_SynchronizationState;
#define btck_SynchronizationState_INIT_REINDEX ((btck_SynchronizationState)(0))
@@ -1102,6 +1132,156 @@ BITCOINKERNEL_API int32_t BITCOINKERNEL_WARN_UNUSED_RESULT btck_chain_get_height
///@}
+/** @name BlockSpentOutputs
+ * Functions for working with block spent outputs.
+ */
+///@{
+
+/**
+ * @brief Reads the block spent coins data the passed in block tree entry points to from
+ * disk and returns it.
+ *
+ * @param[in] chainstate_manager Non-null.
+ * @param[in] block_tree_entry Non-null.
+ * @return The read out block spent outputs, or null on error.
+ */
+BITCOINKERNEL_API btck_BlockSpentOutputs* BITCOINKERNEL_WARN_UNUSED_RESULT btck_block_spent_outputs_read(
+ const btck_ChainstateManager* chainstate_manager,
+ const btck_BlockTreeEntry* block_tree_entry) BITCOINKERNEL_ARG_NONNULL(1, 2);
+
+/**
+ * @brief Copy a block's spent outputs.
+ *
+ * @param[in] block_spent_outputs Non-null.
+ * @return The copied block spent outputs.
+ */
+BITCOINKERNEL_API btck_BlockSpentOutputs* BITCOINKERNEL_WARN_UNUSED_RESULT btck_block_spent_outputs_copy(
+ const btck_BlockSpentOutputs* block_spent_outputs) BITCOINKERNEL_ARG_NONNULL(1);
+
+/**
+ * @brief Returns the number of transaction spent outputs whose data is contained in
+ * block spent outputs.
+ *
+ * @param[in] block_spent_outputs Non-null.
+ * @return The number of transaction spent outputs data in the block spent outputs.
+ */
+BITCOINKERNEL_API size_t BITCOINKERNEL_WARN_UNUSED_RESULT btck_block_spent_outputs_count(
+ const btck_BlockSpentOutputs* block_spent_outputs) BITCOINKERNEL_ARG_NONNULL(1);
+
+/**
+ * @brief Returns a transaction spent outputs contained in the block spent
+ * outputs at a certain index. The returned pointer is unowned and only valid
+ * for the lifetime of block_spent_outputs.
+ *
+ * @param[in] block_spent_outputs Non-null.
+ * @param[in] transaction_spent_outputs_index The index of the transaction spent outputs within the block spent outputs.
+ * @return A transaction spent outputs pointer.
+ */
+BITCOINKERNEL_API const btck_TransactionSpentOutputs* BITCOINKERNEL_WARN_UNUSED_RESULT btck_block_spent_outputs_get_transaction_spent_outputs_at(
+ const btck_BlockSpentOutputs* block_spent_outputs,
+ size_t transaction_spent_outputs_index) BITCOINKERNEL_ARG_NONNULL(1);
+
+/**
+ * Destroy the block spent outputs.
+ */
+BITCOINKERNEL_API void btck_block_spent_outputs_destroy(btck_BlockSpentOutputs* block_spent_outputs);
+
+///@}
+
+/** @name TransactionSpentOutputs
+ * Functions for working with the spent coins of a transaction
+ */
+///@{
+
+/**
+ * @brief Copy a transaction's spent outputs.
+ *
+ * @param[in] transaction_spent_outputs Non-null.
+ * @return The copied transaction spent outputs.
+ */
+BITCOINKERNEL_API btck_TransactionSpentOutputs* BITCOINKERNEL_WARN_UNUSED_RESULT btck_transaction_spent_outputs_copy(
+ const btck_TransactionSpentOutputs* transaction_spent_outputs) BITCOINKERNEL_ARG_NONNULL(1);
+
+/**
+ * @brief Returns the number of previous transaction outputs contained in the
+ * transaction spent outputs data.
+ *
+ * @param[in] transaction_spent_outputs Non-null
+ * @return The number of spent transaction outputs for the transaction.
+ */
+BITCOINKERNEL_API size_t BITCOINKERNEL_WARN_UNUSED_RESULT btck_transaction_spent_outputs_count(
+ const btck_TransactionSpentOutputs* transaction_spent_outputs) BITCOINKERNEL_ARG_NONNULL(1);
+
+/**
+ * @brief Returns a coin contained in the transaction spent outputs at a
+ * certain index. The returned pointer is unowned and only valid for the
+ * lifetime of transaction_spent_outputs.
+ *
+ * @param[in] transaction_spent_outputs Non-null.
+ * @param[in] coin_index The index of the to be retrieved coin within the
+ * transaction spent outputs.
+ * @return A coin pointer.
+ */
+BITCOINKERNEL_API const btck_Coin* BITCOINKERNEL_WARN_UNUSED_RESULT btck_transaction_spent_outputs_get_coin_at(
+ const btck_TransactionSpentOutputs* transaction_spent_outputs,
+ size_t coin_index) BITCOINKERNEL_ARG_NONNULL(1);
+
+/**
+ * Destroy the transaction spent outputs.
+ */
+BITCOINKERNEL_API void btck_transaction_spent_outputs_destroy(btck_TransactionSpentOutputs* transaction_spent_outputs);
+
+///@}
+
+/** @name Coin
+ * Functions for working with coins.
+ */
+///@{
+
+/**
+ * @brief Copy a coin.
+ *
+ * @param[in] coin Non-null.
+ * @return The copied coin.
+ */
+BITCOINKERNEL_API btck_Coin* BITCOINKERNEL_WARN_UNUSED_RESULT btck_coin_copy(
+ const btck_Coin* coin) BITCOINKERNEL_ARG_NONNULL(1);
+
+/**
+ * @brief Returns the height of the block that contains the coin's prevout.
+ *
+ * @param[in] coin Non-null.
+ * @return The block height of the coin.
+ */
+BITCOINKERNEL_API uint32_t BITCOINKERNEL_WARN_UNUSED_RESULT btck_coin_confirmation_height(
+ const btck_Coin* coin) BITCOINKERNEL_ARG_NONNULL(1);
+
+/**
+ * @brief Returns whether the containing transaction was a coinbase.
+ *
+ * @param[in] coin Non-null.
+ * @return 1 if the coin is a coinbase coin, 0 otherwise.
+ */
+BITCOINKERNEL_API int BITCOINKERNEL_WARN_UNUSED_RESULT btck_coin_is_coinbase(
+ const btck_Coin* coin) BITCOINKERNEL_ARG_NONNULL(1);
+
+/**
+ * @brief Return the transaction output of a coin. The returned pointer is
+ * unowned and only valid for the lifetime of the coin.
+ *
+ * @param[in] coin Non-null.
+ * @return A transaction output pointer.
+ */
+BITCOINKERNEL_API const btck_TransactionOutput* BITCOINKERNEL_WARN_UNUSED_RESULT btck_coin_get_output(
+ const btck_Coin* coin) BITCOINKERNEL_ARG_NONNULL(1);
+
+/**
+ * Destroy the coin.
+ */
+BITCOINKERNEL_API void btck_coin_destroy(btck_Coin* coin);
+
+///@}
+
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
diff --git a/src/kernel/bitcoinkernel_wrapper.h b/src/kernel/bitcoinkernel_wrapper.h
index 983a5e07..d9cacab8 100644
--- a/src/kernel/bitcoinkernel_wrapper.h
+++ b/src/kernel/bitcoinkernel_wrapper.h
@@ -790,6 +790,105 @@ public:
}
};
+template <typename Derived>
+class CoinApi
+{
+private:
+ auto impl() const
+ {
+ return static_cast<const Derived*>(this)->get();
+ }
+
+ friend Derived;
+ CoinApi() = default;
+
+public:
+ uint32_t GetConfirmationHeight() const { return btck_coin_confirmation_height(impl()); }
+
+ bool IsCoinbase() const { return btck_coin_is_coinbase(impl()) == 1; }
+
+ TransactionOutputView GetOutput() const
+ {
+ return TransactionOutputView{btck_coin_get_output(impl())};
+ }
+};
+
+class CoinView : public View<btck_Coin>, public CoinApi<CoinView>
+{
+public:
+ explicit CoinView(const btck_Coin* ptr) : View{ptr} {}
+};
+
+class Coin : public Handle<btck_Coin, btck_coin_copy, btck_coin_destroy>, public CoinApi<Coin>
+{
+public:
+ Coin(btck_Coin* coin) : Handle{coin} {}
+
+ Coin(const CoinView& view) : Handle{view} {}
+};
+
+template <typename Derived>
+class TransactionSpentOutputsApi
+{
+private:
+ auto impl() const
+ {
+ return static_cast<const Derived*>(this)->get();
+ }
+
+ friend Derived;
+ TransactionSpentOutputsApi() = default;
+
+public:
+ size_t Count() const
+ {
+ return btck_transaction_spent_outputs_count(impl());
+ }
+
+ CoinView GetCoin(size_t index) const
+ {
+ return CoinView{btck_transaction_spent_outputs_get_coin_at(impl(), index)};
+ }
+
+ MAKE_RANGE_METHOD(Coins, Derived, &TransactionSpentOutputsApi<Derived>::Count, &TransactionSpentOutputsApi<Derived>::GetCoin, *static_cast<const Derived*>(this))
+};
+
+class TransactionSpentOutputsView : public View<btck_TransactionSpentOutputs>, public TransactionSpentOutputsApi<TransactionSpentOutputsView>
+{
+public:
+ explicit TransactionSpentOutputsView(const btck_TransactionSpentOutputs* ptr) : View{ptr} {}
+};
+
+class TransactionSpentOutputs : public Handle<btck_TransactionSpentOutputs, btck_transaction_spent_outputs_copy, btck_transaction_spent_outputs_destroy>,
+ public TransactionSpentOutputsApi<TransactionSpentOutputs>
+{
+public:
+ TransactionSpentOutputs(btck_TransactionSpentOutputs* transaction_spent_outputs) : Handle{transaction_spent_outputs} {}
+
+ TransactionSpentOutputs(const TransactionSpentOutputsView& view) : Handle{view} {}
+};
+
+class BlockSpentOutputs : public Handle<btck_BlockSpentOutputs, btck_block_spent_outputs_copy, btck_block_spent_outputs_destroy>
+{
+public:
+ BlockSpentOutputs(btck_BlockSpentOutputs* block_spent_outputs)
+ : Handle{block_spent_outputs}
+ {
+ }
+
+ size_t Count() const
+ {
+ return btck_block_spent_outputs_count(get());
+ }
+
+ TransactionSpentOutputsView GetTxSpentOutputs(size_t tx_undo_index) const
+ {
+ return TransactionSpentOutputsView{btck_block_spent_outputs_get_transaction_spent_outputs_at(get(), tx_undo_index)};
+ }
+
+ MAKE_RANGE_METHOD(TxsSpentOutputs, BlockSpentOutputs, &BlockSpentOutputs::Count, &BlockSpentOutputs::GetTxSpentOutputs, *this)
+};
+
class ChainMan : UniqueHandle<btck_ChainstateManager, btck_chainstate_manager_destroy>
{
public:
@@ -831,6 +930,11 @@ public:
if (!block) return std::nullopt;
return block;
}
+
+ BlockSpentOutputs ReadBlockSpentOutputs(const BlockTreeEntry& entry) const
+ {
+ return btck_block_spent_outputs_read(get(), entry.get());
+ }
};
} // namespace btck
diff --git a/src/test/kernel/test_kernel.cpp b/src/test/kernel/test_kernel.cpp
index 29765812..525c5f9f 100644
--- a/src/test/kernel/test_kernel.cpp
+++ b/src/test/kernel/test_kernel.cpp
@@ -701,11 +701,13 @@ void chainman_mainnet_validation_test(TestDirectory& test_directory)
check_equal(read_block.value().ToBytes(), raw_block);
// Check that we can read the previous block
- auto tip_2{tip.GetPrevious()};
- auto read_block_2{chainman->ReadBlock(tip_2.value())};
+ BlockTreeEntry tip_2{*tip.GetPrevious()};
+ Block read_block_2{*chainman->ReadBlock(tip_2)};
+ BOOST_CHECK_EQUAL(chainman->ReadBlockSpentOutputs(tip_2).Count(), 0);
+ BOOST_CHECK_EQUAL(chainman->ReadBlockSpentOutputs(tip).Count(), 0);
// It should be an error if we go another block back, since the genesis has no ancestor
- BOOST_CHECK(!tip_2.value().GetPrevious());
+ BOOST_CHECK(!tip_2.GetPrevious());
// If we try to validate it again, it should be a duplicate
BOOST_CHECK(chainman->ProcessBlock(block, &new_block));
@@ -782,6 +784,48 @@ BOOST_AUTO_TEST_CASE(btck_chainman_regtest_tests)
auto read_block_2 = chainman->ReadBlock(tip_2).value();
check_equal(read_block_2.ToBytes(), hex_string_to_byte_vec(REGTEST_BLOCK_DATA[REGTEST_BLOCK_DATA.size() - 2]));
+ // Read spent outputs for current tip and its previous block
+ BlockSpentOutputs block_spent_outputs{chainman->ReadBlockSpentOutputs(tip)};
+ BlockSpentOutputs block_spent_outputs_prev{chainman->ReadBlockSpentOutputs(*tip.GetPrevious())};
+ CheckHandle(block_spent_outputs, block_spent_outputs_prev);
+ CheckRange(block_spent_outputs_prev.TxsSpentOutputs(), block_spent_outputs_prev.Count());
+ BOOST_CHECK_EQUAL(block_spent_outputs.Count(), 1);
+
+ // Get transaction spent outputs from the last transaction in the two blocks
+ TransactionSpentOutputsView transaction_spent_outputs{block_spent_outputs.GetTxSpentOutputs(block_spent_outputs.Count() - 1)};
+ TransactionSpentOutputs owned_transaction_spent_outputs{transaction_spent_outputs};
+ TransactionSpentOutputs owned_transaction_spent_outputs_prev{block_spent_outputs_prev.GetTxSpentOutputs(block_spent_outputs_prev.Count() - 1)};
+ CheckHandle(owned_transaction_spent_outputs, owned_transaction_spent_outputs_prev);
+ CheckRange(transaction_spent_outputs.Coins(), transaction_spent_outputs.Count());
+
+ // Get the last coin from the transaction spent outputs
+ CoinView coin{transaction_spent_outputs.GetCoin(transaction_spent_outputs.Count() - 1)};
+ BOOST_CHECK(!coin.IsCoinbase());
+ Coin owned_coin{coin};
+ Coin owned_coin_prev{owned_transaction_spent_outputs_prev.GetCoin(owned_transaction_spent_outputs_prev.Count() - 1)};
+ CheckHandle(owned_coin, owned_coin_prev);
+
+ // Validate coin properties
+ TransactionOutputView output = coin.GetOutput();
+ uint32_t coin_height = coin.GetConfirmationHeight();
+ BOOST_CHECK_EQUAL(coin_height, 205);
+ BOOST_CHECK_EQUAL(output.Amount(), 100000000);
+
+ // Test script pubkey serialization
+ auto script_pubkey = output.GetScriptPubkey();
+ auto script_pubkey_bytes{script_pubkey.ToBytes()};
+ BOOST_CHECK_EQUAL(script_pubkey_bytes.size(), 22);
+ auto round_trip_script_pubkey{ScriptPubkey(script_pubkey_bytes)};
+ BOOST_CHECK_EQUAL(round_trip_script_pubkey.ToBytes().size(), 22);
+
+ for (const auto tx_spent_outputs : block_spent_outputs.TxsSpentOutputs()) {
+ for (const auto coins : tx_spent_outputs.Coins()) {
+ BOOST_CHECK_GT(coins.GetOutput().Amount(), 1);
+ }
+ }
+
std::filesystem::remove_all(test_directory.m_directory / "blocks" / "blk00000.dat");
- BOOST_CHECK(!chainman->ReadBlock(tip_2));
+ BOOST_CHECK(!chainman->ReadBlock(tip_2).has_value());
+ std::filesystem::remove_all(test_directory.m_directory / "blocks" / "rev00000.dat");
+ BOOST_CHECK_THROW(chainman->ReadBlockSpentOutputs(tip), std::runtime_error);
}
Why this scored 23/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.