Skip to content

smite: mine consensus-valid transactions that violate mempool policy#137

Open
NishantBansal2003 wants to merge 1 commit into
morehouse:masterfrom
NishantBansal2003:bitcoind-fixes
Open

smite: mine consensus-valid transactions that violate mempool policy#137
NishantBansal2003 wants to merge 1 commit into
morehouse:masterfrom
NishantBansal2003:bitcoind-fixes

Conversation

@NishantBansal2003

Copy link
Copy Markdown
Contributor

There were a couple of crashes while I was running the funding-flow generator, where we were getting panics in smite because of the restrictive assert statements we have set. These commits fix those issues in smite.

So now we can’t create two transactions with overlapping inputs. We return early when signing or broadcasting a transaction if it has already been broadcast and confirmed, since the wallet can’t sign a transaction whose inputs have already been spent. There were also a couple of crashes around broadcasting, where either funding_satoshis itself was below the dust limit, or the feerate used to calculate the transaction fee was too low compared to bitcoind default policy. So now we reject those inputs as well.

These cases do not really contribute to fuzzing, because in practice, if bitcoind default policy rejects them, Lightning nodes should also reject those open_channel messages before sending accept_channel. But let’s say they are not rejected, then I think those cases can be caught once we add the open_channel and accept_channel oracle. So in either case, we don’t need to relax bitcoind default policy to exercise the full funding flow

@ekzyis ekzyis left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Only looked at the first commit (5644b83).

I wonder if we also need to unlock the utxos in some cases, like when we fail to broadcast for some reason. Or is that not needed because of snapshot fuzzing, so for every run, bitcoind is also in the same initial state with the utxos unlocked?

But then I wonder why we even need to lock utxos: when would we create two funding transactions with overlapping inputs? Aren't we only creating one funding transaction per run?

@erickcestari

erickcestari commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

There were a couple of crashes while I was running the funding-flow generator, where we were getting panics in smite because of the restrictive assert statements we have set. These commits fix those issues in smite.

My guess is that a crash can still happens when broadcasting the transaction on chain, because it might try to broadcast the same transaction that was mined before which would make the sendrawtransaction rpc call fail and crash the program.

We could ignore such error and add a special case in the minimizer to remove a BroadcastTransaction operation if it broadcast the same transaction twice, since that would be essentially dead code.

edit: Nevermind, I just saw the second commit that avoid broadcasting the same transaction twice. Which makes a lot of sense, and since is very lightweight operation we maybe don't need to minimize it?

But then I wonder why we even need to lock utxos: when would we create two funding transactions with overlapping inputs? Aren't we only creating one funding transaction per run?

I think smite will be able to open multiple channels against the target. Right now (in current master) Smite cannot open a channel, since it misses the funding and broadcast operations of the open channel flow in the generators.

@NishantBansal2003

Copy link
Copy Markdown
Contributor Author

I wonder if we also need to unlock the utxos in some cases, like when we fail to broadcast for some reason. Or is that not needed because of snapshot fuzzing, so for every run, bitcoind is also in the same initial state with the utxos unlocked?

Yep, I think bitcoind will return to the same initial state with a block height of 101, as captured in the snapshot

But then I wonder why we even need to lock utxos: when would we create two funding transactions with overlapping inputs? Aren't we only creating one funding transaction per run?

We have GeneratorInsertionMutator, and eventually have SpliceMutator, which will merge two programs. At that point, we’ll have more than one create funding tx operations

@erickcestari

Copy link
Copy Markdown
Contributor

smite: reject non-relayable funding transactions
Reject funding transactions that would not be accepted
under Bitcoin's default relay policy. Funding construction
now fails for feerates below the minimum relay feerate and
funding outputs below the dust threshold, avoiding
transactions that cannot be relayed or mined by default

Should we care whether the transaction follows the bitcoin core relayable Policy? I think that, if the transaction is consensus-valid, Smite should be able to include it in a block, which would help us cover more edge cases.

@NishantBansal2003

Copy link
Copy Markdown
Contributor Author

since is very lightweight operation we maybe don't need to minimize it?

I think any operation that makes redundant or unnecessary RPC calls, should be minimized, since they are time-consuming and actually hurt fuzzing effectiveness

Should we care whether the transaction follows the bitcoin core relayable Policy? I think that, if the transaction is consensus-valid, Smite should be able to include it in a block, which would help us cover more edge cases.

Some reasons I thought against it are:

  • funding tx below the dust limit or with a very low fee rate will not actually be mined on mainnet
  • Another thing is that currently the bitcoind backend is shared across smite and the target, so changing the policy could mean the target consults bitcoind to check whether the tx is broadcastable, and bitcoind could say yes. I was thinking of using the default rules for both the target and bitcoind
  • In the future, we’re going to have an open_channel <-> accept_channel oracle, which will itself flag the case if it isn't rejected by the target, so there is no need to go beyond that point to find those issues for now

But I don’t have a strong opinion, so I’m fine if we want to relax the bitcoind rules instead. Let me know

@ekzyis

ekzyis commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

funding tx below the dust limit or with a very low fee rate will not actually be mined on mainnet

It could get mined with out-of-band payments, or because it's the miner's own tx etc.

They are non-standard according to Bitcoin Core, but they can get mined.

I agree with @erickcestari, I think never broadcasting txs below the dust limit would unnecessarily limit fuzzing effectiveness, or at least I’m not convinced yet why we shouldn’t broadcast them.

@erickcestari

Copy link
Copy Markdown
Contributor
  • In the future, we’re going to have an open_channel <-> accept_channel oracle, which will itself flag the case if it isn't rejected by the target, so there is no need to go beyond that point to find those issues for now

I think I got what @NishantBansal2003 said. If a lightning node is building transactions (funding transactions, commitment transactions, HTLC transactions) that aren't accepted by mempool (dust output, nonstandard script, below static minrelay) this is already a bug that should be reported and the fuzzer should crash with help of oracles.

@morehouse morehouse left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

The first three commits LGTM. I'd be happy to merge them today as a separate PR.

I'm less sure about the last commit. I agree that we don't want to change the default mempool policy used by the target node, but I think it could be quite profitable to preserve the ability to mine non-standard transactions that may trigger edge cases on the target node.

One way to reconcile the two is to use separate bitcoinds for smite and the target. Of course then the bitcoind nodes communicating with each other would add a new source of latency.

Alternatively, we could keep the shared bitcoind and then if sendrawtransaction fails due to some mempool policy rejection, we could fallback to mining the transaction directly with generateblock instead.

Comment thread smite/src/bitcoin.rs Outdated
The fuzzer may build transactions that are consensus-valid
but violate mempool policy, such as those below the minimum
relay feerate. sendrawtransaction refuses these, so check
testmempoolaccept first and mine the transaction directly
with generateblock when it does not meet mempool policy.

Signed-off-by: Nishant Bansal <nishant.bansal.282003@gmail.com>
@NishantBansal2003 NishantBansal2003 changed the title smite: make funding and broadcast respect bitcoind default policy smite: mine consensus-valid transactions that violate mempool policy Jul 10, 2026
@NishantBansal2003

Copy link
Copy Markdown
Contributor Author

Updated the last commit to mine the tx directly if it violates mempool policy while still being consensus-valid in: c193003

@NishantBansal2003 NishantBansal2003 marked this pull request as ready for review July 10, 2026 04:27

@morehouse morehouse left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

My main concern is that now the BroadcastTransaction operation may silently mine the transaction as well instead of waiting for MineBlocks to do that.

WDYT about an approach like this?

  • If the broadcast fails due to non-standardness, the executor saves the raw tx in it's own "private mempool"
  • On the next MineBlocks operation, if the private mempool contains anything, the executor appends its private mempool to bitcoind's mempool from getrawmempool and mines the combined mempool in the first block with generateblock, then mines the remaining blocks normally.

Comment thread smite/src/bitcoin.rs

// If the transaction follows mempool policy, broadcast it normally.
// Otherwise, mine it directly into a block.
if !self.accepted_by_mempool(&signed_tx.hex) {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Rather than doing another RPC call, can we just check the output from sendrawtransaction to save some overhead?

Comment thread smite/src/bitcoin.rs
// If the transaction follows mempool policy, broadcast it normally.
// Otherwise, mine it directly into a block.
if !self.accepted_by_mempool(&signed_tx.hex) {
self.generate_block_with_tx(&signed_tx.hex);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

So now our BroadcastTransaction operation may also mine blocks. Maybe this is okay as a practicality, though it would be nice to only do the mining in the actual MineBlocks operation...

One unexpected example: previously if we broadcast the same transaction twice it was a no-op. Now if we do that the transaction gets automatically mined on the second broadcast.

Comment thread smite/src/bitcoin.rs
.run()
.arg("testmempoolaccept")
.arg(&txs)
.arg("0")

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Suggested change
.arg("0")
// Disable the high-feerate cap and accept any fee rate for broadcast.
.arg("0")

Comment thread smite/src/bitcoin.rs
Comment on lines +234 to +237
assert!(
self.get_transaction_confirmations(txid) > 0,
"generateblock did not mine transaction {txid}"
);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Is this check necessary -- is there any situation where generateblock succeeds but fails to mine our transaction?

If not, let's drop this check to save an RPC call.

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.

4 participants