Skip to content
This repository was archived by the owner on Jul 5, 2024. It is now read-only.

Commit 8755e44

Browse files
Add basic invalid tx support (#1677)
### Description Resolves #1611 Upstreamed from taikoxyz#157 with some additional changes. Adds an additional execution state for transactions that are invalid and need to be skipped. This execution state needs to be enabled instead of the normal BeginTx/EndTx for valid transactions. Support skipping the following invalid transactions for now: - nonce too low - nonce too high - intrinsic gas too low - insufficient funds for gas * price + value More cases will be added in future PRs. ### Issue Link #1611 ### Type of change - [ ] Bug fix (non-breaking change which fixes an issue) - [x] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] This change requires a documentation update ### Contents - [_item_] ### Rationale [_design decisions and extended information_] ### How Has This Been Tested? - Added required unit tests - make test-all passes --------- Co-authored-by: Cecilia Zhang <[email protected]>
1 parent e246d4d commit 8755e44

File tree

25 files changed

+1056
-322
lines changed

25 files changed

+1056
-322
lines changed

Cargo.lock

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bus-mapping/src/circuit_input_builder.rs

+29-20
Original file line numberDiff line numberDiff line change
@@ -250,28 +250,37 @@ impl<'a, C: CircuitsParams> CircuitInputBuilder<C> {
250250
let mut tx = self.new_tx(tx_index, eth_tx, !geth_trace.failed)?;
251251
let mut tx_ctx = TransactionContext::new(eth_tx, geth_trace, is_last_tx)?;
252252

253-
// Generate BeginTx step
254-
let begin_tx_step = gen_associated_steps(
255-
&mut self.state_ref(&mut tx, &mut tx_ctx),
256-
ExecState::BeginTx,
257-
)?;
258-
tx.steps_mut().push(begin_tx_step);
259-
260-
for (index, geth_step) in geth_trace.struct_logs.iter().enumerate() {
261-
let mut state_ref = self.state_ref(&mut tx, &mut tx_ctx);
262-
log::trace!("handle {}th opcode {:?} ", index, geth_step.op);
263-
let exec_steps = gen_associated_ops(
264-
&geth_step.op,
265-
&mut state_ref,
266-
&geth_trace.struct_logs[index..],
253+
if !geth_trace.invalid {
254+
// Generate BeginTx step
255+
let begin_tx_step = gen_associated_steps(
256+
&mut self.state_ref(&mut tx, &mut tx_ctx),
257+
ExecState::BeginTx,
267258
)?;
268-
tx.steps_mut().extend(exec_steps);
269-
}
259+
tx.steps_mut().push(begin_tx_step);
260+
261+
for (index, geth_step) in geth_trace.struct_logs.iter().enumerate() {
262+
let mut state_ref = self.state_ref(&mut tx, &mut tx_ctx);
263+
log::trace!("handle {}th opcode {:?} ", index, geth_step.op);
264+
let exec_steps = gen_associated_ops(
265+
&geth_step.op,
266+
&mut state_ref,
267+
&geth_trace.struct_logs[index..],
268+
)?;
269+
tx.steps_mut().extend(exec_steps);
270+
}
270271

271-
// Generate EndTx step
272-
let end_tx_step =
273-
gen_associated_steps(&mut self.state_ref(&mut tx, &mut tx_ctx), ExecState::EndTx)?;
274-
tx.steps_mut().push(end_tx_step);
272+
// Generate EndTx step
273+
let end_tx_step =
274+
gen_associated_steps(&mut self.state_ref(&mut tx, &mut tx_ctx), ExecState::EndTx)?;
275+
tx.steps_mut().push(end_tx_step);
276+
} else {
277+
// Generate InvalidTx step
278+
let invalid_tx_step = gen_associated_steps(
279+
&mut self.state_ref(&mut tx, &mut tx_ctx),
280+
ExecState::InvalidTx,
281+
)?;
282+
tx.steps_mut().push(invalid_tx_step);
283+
}
275284

276285
self.sdb.commit_tx();
277286
self.block.txs.push(tx);

bus-mapping/src/circuit_input_builder/execution.rs

+2
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ pub enum ExecState {
130130
EndTx,
131131
/// Virtual step End Block
132132
EndBlock,
133+
/// Invalid Tx
134+
InvalidTx,
133135
}
134136

135137
impl Default for ExecState {

bus-mapping/src/circuit_input_builder/input_state_ref.rs

+10
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,16 @@ impl<'a> CircuitInputStateRef<'a> {
5858
))
5959
}
6060

61+
/// Create a new InvalidTx step
62+
pub fn new_invalid_tx_step(&self) -> ExecStep {
63+
ExecStep {
64+
exec_state: ExecState::InvalidTx,
65+
gas_left: self.tx.gas(),
66+
rwc: self.block_ctx.rwc,
67+
..Default::default()
68+
}
69+
}
70+
6171
/// Create a new BeginTx step
6272
pub fn new_begin_tx_step(&self) -> ExecStep {
6373
ExecStep {

bus-mapping/src/circuit_input_builder/tracer_tests.rs

+86
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use eth_types::{
1616
};
1717
use lazy_static::lazy_static;
1818
use mock::{
19+
eth,
1920
test_ctx::{helpers::*, LoggerConfig, TestContext},
2021
MOCK_COINBASE,
2122
};
@@ -44,6 +45,7 @@ impl CircuitInputBuilderTx {
4445
&GethExecTrace {
4546
gas: 0,
4647
failed: false,
48+
invalid: false,
4749
return_value: "".to_owned(),
4850
struct_logs: vec![geth_step.clone()],
4951
},
@@ -339,6 +341,90 @@ fn tracer_call_success() {
339341
assert_eq!(error.unwrap(), None);
340342
}
341343

344+
#[test]
345+
fn tracer_invalid_tx_invalid_nonce() {
346+
let code_a = bytecode! {
347+
PUSH1(0x0)
348+
};
349+
350+
// Get the execution steps from the external tracer
351+
let block: GethData = TestContext::<3, 1>::new(
352+
None,
353+
|accs| {
354+
accs[0]
355+
.address(address!("0x0000000000000000000000000000000000000000"))
356+
.code(code_a);
357+
accs[1]
358+
.address(address!("0x000000000000000000000000000000000cafe001"))
359+
.nonce(3);
360+
},
361+
|mut txs, accs| {
362+
txs[0]
363+
.to(accs[0].address)
364+
.from(accs[1].address)
365+
.invalid()
366+
.set_nonce(1);
367+
},
368+
|block, _tx| block.number(0xcafeu64),
369+
)
370+
.unwrap()
371+
.into();
372+
373+
assert!(block.geth_traces[0].invalid);
374+
}
375+
376+
#[test]
377+
fn tracer_invalid_tx_insufficient_gas() {
378+
// Get the execution steps from the external tracer
379+
let block: GethData = TestContext::<3, 1>::new(
380+
None,
381+
|accs| {
382+
accs[0].address(address!("0x0000000000000000000000000000000000000000"));
383+
accs[1]
384+
.address(address!("0x000000000000000000000000000000000cafe001"))
385+
.balance(eth(1));
386+
},
387+
|mut txs, accs| {
388+
txs[0]
389+
.to(accs[0].address)
390+
.from(accs[1].address)
391+
.gas(Word::from(20_000))
392+
.invalid();
393+
},
394+
|block, _tx| block.number(0xcafeu64),
395+
)
396+
.unwrap()
397+
.into();
398+
399+
assert!(block.geth_traces[0].invalid);
400+
}
401+
402+
#[test]
403+
fn tracer_invalid_tx_insufficient_balance() {
404+
// Get the execution steps from the external tracer
405+
let block: GethData = TestContext::<3, 1>::new(
406+
None,
407+
|accs| {
408+
accs[0].address(address!("0x0000000000000000000000000000000000000000"));
409+
accs[1]
410+
.address(address!("0x000000000000000000000000000000000cafe001"))
411+
.balance(Word::from(1));
412+
},
413+
|mut txs, accs| {
414+
txs[0]
415+
.to(accs[0].address)
416+
.from(accs[1].address)
417+
.value(100.into())
418+
.invalid();
419+
},
420+
|block, _tx| block.number(0xcafeu64),
421+
)
422+
.unwrap()
423+
.into();
424+
425+
assert!(block.geth_traces[0].invalid);
426+
}
427+
342428
#[test]
343429
fn tracer_err_address_collision() {
344430
// We do CREATE2 twice with the same parameters, with a code_creater

bus-mapping/src/evm/opcodes.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ mod extcodecopy;
2828
mod extcodehash;
2929
mod extcodesize;
3030
mod gasprice;
31+
mod invalid_tx;
3132
mod logs;
3233
mod mload;
3334
mod mstore;
@@ -60,7 +61,7 @@ mod error_write_protection;
6061
#[cfg(test)]
6162
mod memory_expansion_test;
6263

63-
use self::sha3::Sha3;
64+
use self::{invalid_tx::InvalidTx, sha3::Sha3};
6465
use address::Address;
6566
use balance::Balance;
6667
use begin_end_tx::BeginEndTx;
@@ -408,6 +409,7 @@ pub fn gen_associated_steps(
408409
) -> Result<ExecStep, Error> {
409410
let fn_gen_associated_steps = match execution_step {
410411
ExecState::BeginTx | ExecState::EndTx => BeginEndTx::gen_associated_steps,
412+
ExecState::InvalidTx => InvalidTx::gen_associated_steps,
411413
_ => {
412414
unreachable!()
413415
}

bus-mapping/src/evm/opcodes/begin_end_tx.rs

+54-17
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::TxExecSteps;
22
use crate::{
3-
circuit_input_builder::{CircuitInputStateRef, ExecState, ExecStep},
3+
circuit_input_builder::{Call, CircuitInputStateRef, ExecState, ExecStep},
44
operation::{AccountField, AccountOp, CallContextField, TxReceiptField, TxRefundOp, RW},
55
state_db::CodeDB,
66
Error,
@@ -33,8 +33,9 @@ fn gen_begin_tx_steps(state: &mut CircuitInputStateRef) -> Result<ExecStep, Erro
3333
let mut exec_step = state.new_begin_tx_step();
3434
let call = state.call()?.clone();
3535

36+
begin_tx(state, &mut exec_step, &call)?;
37+
3638
for (field, value) in [
37-
(CallContextField::TxId, state.tx_ctx.id().into()),
3839
(
3940
CallContextField::RwCounterEndOfReversion,
4041
call.rw_counter_end_of_reversion.into(),
@@ -313,17 +314,62 @@ fn gen_end_tx_steps(state: &mut CircuitInputStateRef) -> Result<ExecStep, Error>
313314
false,
314315
)?;
315316

317+
end_tx(state, &mut exec_step, &call)?;
318+
319+
Ok(exec_step)
320+
}
321+
322+
pub(crate) fn begin_tx(
323+
state: &mut CircuitInputStateRef,
324+
exec_step: &mut ExecStep,
325+
call: &Call,
326+
) -> Result<(), Error> {
327+
// Write the transaction id
328+
state.call_context_write(
329+
exec_step,
330+
call.call_id,
331+
CallContextField::TxId,
332+
state.tx_ctx.id().into(),
333+
)?;
334+
Ok(())
335+
}
336+
337+
pub(crate) fn end_tx(
338+
state: &mut CircuitInputStateRef,
339+
exec_step: &mut ExecStep,
340+
call: &Call,
341+
) -> Result<(), Error> {
342+
// Write the tx receipt
343+
write_tx_receipt(state, exec_step, call.is_persistent)?;
344+
345+
// Write the next transaction id if we're not at the last tx
346+
if !state.tx_ctx.is_last_tx() {
347+
state.call_context_write(
348+
exec_step,
349+
state.block_ctx.rwc.0 + 1,
350+
CallContextField::TxId,
351+
(state.tx_ctx.id() + 1).into(),
352+
)?;
353+
}
354+
Ok(())
355+
}
356+
357+
fn write_tx_receipt(
358+
state: &mut CircuitInputStateRef,
359+
exec_step: &mut ExecStep,
360+
is_persistent: bool,
361+
) -> Result<(), Error> {
316362
// handle tx receipt tag
317363
state.tx_receipt_write(
318-
&mut exec_step,
364+
exec_step,
319365
state.tx_ctx.id(),
320366
TxReceiptField::PostStateOrStatus,
321-
call.is_persistent as u64,
367+
is_persistent as u64,
322368
)?;
323369

324370
let log_id = exec_step.log_id;
325371
state.tx_receipt_write(
326-
&mut exec_step,
372+
exec_step,
327373
state.tx_ctx.id(),
328374
TxReceiptField::LogLength,
329375
log_id as u64,
@@ -332,7 +378,7 @@ fn gen_end_tx_steps(state: &mut CircuitInputStateRef) -> Result<ExecStep, Error>
332378
if state.tx_ctx.id() > 1 {
333379
// query pre tx cumulative gas
334380
state.tx_receipt_read(
335-
&mut exec_step,
381+
exec_step,
336382
state.tx_ctx.id() - 1,
337383
TxReceiptField::CumulativeGasUsed,
338384
state.block_ctx.cumulative_gas_used,
@@ -341,20 +387,11 @@ fn gen_end_tx_steps(state: &mut CircuitInputStateRef) -> Result<ExecStep, Error>
341387

342388
state.block_ctx.cumulative_gas_used += state.tx.gas() - exec_step.gas_left;
343389
state.tx_receipt_write(
344-
&mut exec_step,
390+
exec_step,
345391
state.tx_ctx.id(),
346392
TxReceiptField::CumulativeGasUsed,
347393
state.block_ctx.cumulative_gas_used,
348394
)?;
349395

350-
if !state.tx_ctx.is_last_tx() {
351-
state.call_context_write(
352-
&mut exec_step,
353-
state.block_ctx.rwc.0 + 1,
354-
CallContextField::TxId,
355-
(state.tx_ctx.id() + 1).into(),
356-
)?;
357-
}
358-
359-
Ok(exec_step)
396+
Ok(())
360397
}
+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
use super::{
2+
begin_end_tx::{begin_tx, end_tx},
3+
TxExecSteps,
4+
};
5+
use crate::{
6+
circuit_input_builder::{CircuitInputStateRef, ExecState, ExecStep},
7+
operation::AccountField,
8+
Error,
9+
};
10+
11+
#[derive(Clone, Copy, Debug)]
12+
pub(crate) struct InvalidTx;
13+
14+
impl TxExecSteps for InvalidTx {
15+
fn gen_associated_steps(
16+
state: &mut CircuitInputStateRef,
17+
_execution_step: ExecState,
18+
) -> Result<ExecStep, Error> {
19+
let mut exec_step = state.new_invalid_tx_step();
20+
let call = state.call()?.clone();
21+
let caller = call.caller_address;
22+
23+
// Start processing the tx
24+
begin_tx(state, &mut exec_step, &call)?;
25+
26+
// Read the nonce to prove mismatch
27+
state.account_read(
28+
&mut exec_step,
29+
caller,
30+
AccountField::Nonce,
31+
state.sdb.get_account(&caller).1.nonce.into(),
32+
)?;
33+
34+
// Read the balance to compare with intrinsic gas cost + value
35+
state.account_read(
36+
&mut exec_step,
37+
caller,
38+
AccountField::Balance,
39+
state.sdb.get_account(&caller).1.balance,
40+
)?;
41+
42+
// Stop processing the tx
43+
end_tx(state, &mut exec_step, &call)?;
44+
45+
Ok(exec_step)
46+
}
47+
}

0 commit comments

Comments
 (0)