`IndexedTxGraph::apply_block` does not check merkle root
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 78/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- rust
- Domain
- blockchain
Research direction
Start at the IndexedTxGraph::apply_block_relevant and IndexedTxGraph::apply_block entry points and run the reproduction test from the issue. Check how each method handles block txdata versus the header merkle root; done means the documented trust requirement is clear and debug builds fail for the fabricated-body case without changing the public error behavior.
Written by the indexing model from the issue text.
Description
Describe the bug
IndexedTxGraph::apply_block_relevant and IndexedTxGraph::apply_block anchor transactions without verifying that the block’s txdata matches its header Merkle root.
A caller that accepts an unvalidated block body can attach fabricated transactions to a genuine block hash, causing them to appear confirmed.
To Reproduce
#[test]
fn apply_block_relevant_confirms_txs_not_in_merkle_root() {
use bdk_chain::{
local_chain::LocalChain, spk_txout::SpkTxOutIndex, BlockId, IndexedTxGraph,
};
use bitcoin::{
absolute, hashes::Hash, transaction, Amount, Network, OutPoint, ScriptBuf,
Transaction, TxIn, TxOut, Txid,
};
let script = ScriptBuf::new();
let mut graph = IndexedTxGraph::<BlockId, SpkTxOutIndex<()>>::default();
assert!(graph.index.insert_spk((), script.clone()));
let mut block = bitcoin::constants::genesis_block(Network::Bitcoin);
let genuine_hash = block.block_hash();
block.txdata = vec![Transaction {
version: transaction::Version::TWO,
lock_time: absolute::LockTime::ZERO,
input: vec![TxIn {
previous_output: OutPoint::new(Txid::from_byte_array([1; 32]), 0),
..Default::default()
}],
output: vec![TxOut {
value: Amount::from_sat(50_000),
script_pubkey: script,
}],
}];
assert_eq!(block.block_hash(), genuine_hash);
assert!(!block.check_merkle_root());
let chain = LocalChain::from_blocks([(0, genuine_hash)].into_iter().collect()).unwrap();
let _ = graph.apply_block_relevant(&block, 0);
let view = chain.canonical_view(
graph.graph(),
chain.tip().block_id(),
Default::default(),
);
let balance = view.balance(
graph.index.outpoints().iter().cloned(),
|_, _| true,
0,
);
assert_eq!(balance.confirmed, Amount::from_sat(50_000));
}
Expected behavior
Document that these methods trust txdata matches the header merkle root. Callers must check that before calling. Add debug_assert!(block.check_merkle_root()) so the fake-body case fails in debug.
Alternative: Reject on merkle mismatch before updating graph/index. That would be a breaking change (ChangeSet → Result).
Build environment
- BDK tag/commit: acc06e53220960caa89efd5984d7b43914640dd4
Which backend(s) are relevant (if any)?
- None / not backend-related (e.g.
bdk_chain,bdk_core)
Is this blocking production use?
- No
Additional context
The block header remains genuine, so block.block_hash() is unchanged; only its transaction list is replaced. LocalChain therefore accepts the anchor as canonical even though the transactions were never committed by the block header.
- Dominant language
- Rust
- Stars
- 1.1k
- Forks
- 491
- Avg merge
- 1d 5h
- Merged PRs (30d)
- 1
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from bitcoindevkit/bdk
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
bitcoindevkit/bdk#2309 ·
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
bitcoindevkit/bdk#2308 ·
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
bitcoindevkit/bdk#2307 ·
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
bitcoindevkit/bdk#2294 ·
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
bitcoindevkit/bdk#2293 ·
All issues in bitcoindevkit/bdk
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
Eynzof/Hermes-CN-Desktop#610 ·
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
gitbutlerapp/gitbutler#15998 · 1 comment ·
-
bug triage:deciding
Difficulty 1/5 Under an hour Newbie friendliness 88/100
open-telemetry/otel-arrow#4132 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100