Skip to content

smite: retry mine_block_including on mempool eviction - #272

Merged
morehouse merged 1 commit into
lnfuzz:masterfrom
NishantBansal2003:mine-block-race
Sep 29, 2026
Merged

morehouse merged 1 commit into
lnfuzz:masterfrom
NishantBansal2003:mine-block-race

Conversation

@NishantBansal2003

Copy link
Copy Markdown
Contributor

Closes: #237

mine_block_including snapshots the mempool and passes the txids to generateblock. If the target RBF fee-bumps a transaction between the two calls, generateblock fails with not in mempool.

Retry with a fresh mempool snapshot up to 3 times. RBF replacement is atomic, so a single retry should succeed. The extra attempt is a buffer.

mine_block_including snapshots the mempool and passes
the txids to generateblock. If the target RBF fee-bumps a
transaction between the two calls, generateblock fails with
"not in mempool".

Retry with a fresh mempool snapshot up to 3 times. RBF
replacement is atomic, so a single retry should succeed.
The extra attempt is a buffer.

Signed-off-by: Nishant Bansal <nishant.bansal.282003@gmail.com>
Comment thread smite/src/bitcoin.rs
Comment on lines +162 to +185
for attempt in 1..=MAX_ATTEMPTS {
let mut txs = self.get_raw_mempool();
txs.extend_from_slice(private_mempool);
let txs_json = serde_json::to_string(&txs).expect("tx list serializes to valid JSON");

let gen_out = self
.run()
.arg("generateblock")
.arg(address.to_string())
.arg(&txs_json)
.output()
.expect("bitcoin-cli generateblock should not fail");
if gen_out.status.success() {
return;
}

// Only retry if a snapshotted transaction left the mempool (e.g.
// replaced by an RBF fee bump) before the block was generated.
let stderr = String::from_utf8_lossy(&gen_out.stderr);
assert!(
stderr.contains("not in mempool") && attempt < MAX_ATTEMPTS,
"bitcoin-cli generateblock failed: {stderr}"
);
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style nit:

Suggested change
for attempt in 1..=MAX_ATTEMPTS {
let mut txs = self.get_raw_mempool();
txs.extend_from_slice(private_mempool);
let txs_json = serde_json::to_string(&txs).expect("tx list serializes to valid JSON");
let gen_out = self
.run()
.arg("generateblock")
.arg(address.to_string())
.arg(&txs_json)
.output()
.expect("bitcoin-cli generateblock should not fail");
if gen_out.status.success() {
return;
}
// Only retry if a snapshotted transaction left the mempool (e.g.
// replaced by an RBF fee bump) before the block was generated.
let stderr = String::from_utf8_lossy(&gen_out.stderr);
assert!(
stderr.contains("not in mempool") && attempt < MAX_ATTEMPTS,
"bitcoin-cli generateblock failed: {stderr}"
);
}
for attempt in 0..MAX_ATTEMPTS {
let mut txs = self.get_raw_mempool();
txs.extend_from_slice(private_mempool);
let txs_json = serde_json::to_string(&txs).expect("tx list serializes to valid JSON");
let gen_out = self
.run()
.arg("generateblock")
.arg(address.to_string())
.arg(&txs_json)
.output()
.expect("bitcoin-cli generateblock should not fail");
if gen_out.status.success() {
return;
}
// Only retry if a snapshotted transaction left the mempool (e.g.
// replaced by an RBF fee bump) before the block was generated.
let stderr = String::from_utf8_lossy(&gen_out.stderr);
assert!(stderr.contains("not in mempool"), "bitcoin-cli generateblock failed: {stderr}");
}
panic!("bitcoin-cli generateblock failed {MAX_ATTEMPTS} times: mempool kept changing");

@morehouse
morehouse merged commit 7de6f6a into lnfuzz:master Sep 29, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

mine_block_including race

2 participants