smite: mine consensus-valid transactions that violate mempool policy#137
smite: mine consensus-valid transactions that violate mempool policy#137NishantBansal2003 wants to merge 2 commits into
Conversation
ekzyis
left a comment
There was a problem hiding this comment.
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?
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 We could ignore such error and add a special case in the minimizer to remove a 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?
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. |
Yep, I think
We have |
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. |
I think any operation that makes redundant or unnecessary RPC calls, should be minimized, since they are time-consuming and actually hurt fuzzing effectiveness
Some reasons I thought against it are:
But I don’t have a strong opinion, so I’m fine if we want to relax the |
e5dc4e7 to
62fbf6c
Compare
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. |
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
left a comment
There was a problem hiding this comment.
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.
62fbf6c to
c193003
Compare
|
Updated the last commit to mine the tx directly if it violates mempool policy while still being consensus-valid in: c193003 |
morehouse
left a comment
There was a problem hiding this comment.
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
MineBlocksoperation, if the private mempool contains anything, the executor appends its private mempool tobitcoind's mempool fromgetrawmempooland mines the combined mempool in the first block withgenerateblock, then mines the remaining blocks normally.
The fuzzer may build transactions that are consensus-valid but violate mempool policy. `sendrawtransaction` refuses these, so return the raw hex instead of panicking and queue it in a per-executor private mempool, deduplicated on txid. On the next MineBlocks, drain the queue and mine the queued transactions in the first block, including the current mempool so that already-broadcast transactions are not dropped. Signed-off-by: Nishant Bansal <nishant.bansal.282003@gmail.com>
c193003 to
daa8ebb
Compare
Yeah, I think mining blocks only in the |
| /// - If any transaction in `private_mempool` is consensus-invalid. | ||
| fn mine_block_including(&self, private_mempool: &[String]) { | ||
| let mut txs = self.get_raw_mempool(); | ||
| txs.extend(private_mempool.iter().cloned()); |
There was a problem hiding this comment.
Nit:
| txs.extend(private_mempool.iter().cloned()); | |
| txs.extend_from_slice(private_mempool); |
| let address = self.get_new_address(); | ||
| let txs_json = serde_json::to_string(&txs).expect("tx list serializes to valid JSON"); |
There was a problem hiding this comment.
Nit: the txs_json line would fit better in the block above.
| .arg(&txs_json) | ||
| .output() | ||
| .expect("bitcoin-cli generateblock should not fail"); | ||
| assert!( |
There was a problem hiding this comment.
I think this will panic if there's a duplicate tx or if the list isn't topologically ordered. I think we can't really hit that case currently since the private mempool comes after the public one and we deduplicate on txid in the executor.
But we should still probably call out these scenarios in the "Panics" section above.
|
|
||
| impl BitcoinRpc for MockBitcoinCli { | ||
| fn mine_blocks(&mut self, num_blocks: u8) { | ||
| fn mine_blocks(&mut self, num_blocks: u8, _private_mempool: &[String]) { |
There was a problem hiding this comment.
We should be able to easily mock a mempool rejection to test the new private mempool behavior.
| // The mempool rejects non-standard or low-feerate transactions. Return | ||
| // the raw hex so the caller can mine it directly later. | ||
| if !broadcast_out.status.success() { | ||
| return Some(signed_tx.hex); | ||
| } |
There was a problem hiding this comment.
This considers all failures to be due to non-standardness. I'm not sure if we can get any other kinds of failures currently, but in the future maybe we could?
I suppose we'll get a panic later when mining if the failure here is due to something else. Probably that's okay, though debugging may be slightly trickier in that case.
68054f8 to
6c4f87d
Compare
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_satoshisitself was below the dust limit, or the feerate used to calculate the transaction fee was too low compared tobitcoinddefault policy. So now we reject those inputs as well.These cases do not really contribute to fuzzing, because in practice, if
bitcoinddefault policy rejects them, Lightning nodes should also reject thoseopen_channelmessages before sendingaccept_channel. But let’s say they are not rejected, then I think those cases can be caught once we add theopen_channelandaccept_channeloracle. So in either case, we don’t need to relaxbitcoinddefault policy to exercise the full funding flow