Skip to content
This repository has been archived by the owner on May 3, 2024. It is now read-only.

Skip invalid txs due to nonce, intrinsic gas and insufficient funds #115

Merged
merged 44 commits into from
Aug 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
1f0abe8
Add invalid signature support
xiaodino Jun 1, 2023
8a890e1
Update Ref. spec 0. in tx_circuit.rs
xiaodino Jun 4, 2023
9d894b8
Update
xiaodino Jun 5, 2023
d4472d1
Support invalid tx in begin_tx and end_tx
xiaodino Jun 6, 2023
ccca3d5
Update
xiaodino Jun 6, 2023
994d959
Revert changes in begin_tx.rs
xiaodino Jun 8, 2023
cb146a8
Add nonce back
xiaodino Jun 8, 2023
d37638e
Update
xiaodino Jun 8, 2023
de2607e
Update
xiaodino Jun 8, 2023
0f76a4d
Update
xiaodino Jun 8, 2023
352e9be
Add comments
xiaodino Jun 11, 2023
7179baa
Rename mul_gas_fee_by_gas to gas_fee
xiaodino Jun 11, 2023
ab275b0
Debug
xiaodino Jun 11, 2023
a09bc8a
Revert "Rename mul_gas_fee_by_gas to gas_fee"
xiaodino Jun 11, 2023
d3c4997
Update
xiaodino Jun 11, 2023
9229afc
Update
xiaodino Jun 11, 2023
3916fe1
Clean code
xiaodino Jun 12, 2023
35d6b25
Upgrade snark-verifier
xiaodino Jun 12, 2023
fd78941
Fix failed tests
xiaodino Jun 13, 2023
8b67a9e
Update
xiaodino Jun 13, 2023
f585885
Clean test
xiaodino Jun 13, 2023
7c5854a
Update
xiaodino Jun 13, 2023
d59c7b3
Update
xiaodino Jun 13, 2023
87dbc6b
Remove default method in eth-types/src/lib.rs
xiaodino Jun 13, 2023
322cbc2
Remove type
xiaodino Jun 13, 2023
334b25c
Revert formatting in GoLang in trace.go
xiaodino Jun 13, 2023
a81aeb7
Update
xiaodino Jun 13, 2023
b8ff9cf
Addressed comments
xiaodino Jun 15, 2023
2275ce4
Revert changes in evm_circuit/param.rs
xiaodino Jun 15, 2023
df38b6d
Update
xiaodino Jun 18, 2023
7392e62
Update
xiaodino Jun 18, 2023
61126cf
Update
xiaodino Jun 18, 2023
3e73572
Update
xiaodino Jun 18, 2023
ad683fe
Update
xiaodino Jun 19, 2023
0ca5b0d
Update
xiaodino Jun 19, 2023
b203a7d
Support invalid tx
xiaodino Jun 20, 2023
55a3789
Update
xiaodino Jun 21, 2023
1be8147
Update
xiaodino Jun 23, 2023
6b2790b
Merge branch 'main' into xiaodino/invalid-tx-nonce-eth-balance
Brechtpd Jun 25, 2023
656cdf5
Merge branch 'main' into xiaodino/invalid-tx-nonce-eth-balance
xiaodino Jun 26, 2023
4b1781b
Work around zero gas payment to non-existing coinbase address
Brechtpd Jun 27, 2023
196010c
Always read the caller balance in begin_tx
Brechtpd Jun 28, 2023
15bc737
Merge branch 'xiaodino/invalid-tx-nonce-eth-balance' of github.com:ta…
xiaodino Jun 30, 2023
189e66d
Resolve merge conflicts
xiaodino Aug 14, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 10 additions & 3 deletions bus-mapping/src/circuit_input_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ impl<'a> CircuitInputBuilder {
&mut self,
eth_tx: &eth_types::Transaction,
is_success: bool,
is_invalid: bool,
) -> Result<Transaction, Error> {
let call_id = self.block_ctx.rwc.0;

Expand All @@ -167,7 +168,14 @@ impl<'a> CircuitInputBuilder {
),
);

Transaction::new(call_id, &self.sdb, &mut self.code_db, eth_tx, is_success)
Transaction::new(
call_id,
&self.sdb,
&mut self.code_db,
eth_tx,
is_success,
is_invalid,
)
}

/// Iterate over all generated CallContext RwCounterEndOfReversion
Expand Down Expand Up @@ -284,9 +292,8 @@ impl<'a> CircuitInputBuilder {
is_anchor_tx: bool,
is_last_tx: bool,
) -> Result<(), Error> {
let mut tx = self.new_tx(eth_tx, !geth_trace.failed)?;
let mut tx = self.new_tx(eth_tx, !geth_trace.failed, geth_trace.invalid)?;
let mut tx_ctx = TransactionContext::new(eth_tx, geth_trace, is_anchor_tx, is_last_tx)?;

// Generate BeginTx step
let begin_tx_step = gen_associated_steps(
&mut self.state_ref(&mut tx, &mut tx_ctx),
Expand Down
26 changes: 16 additions & 10 deletions bus-mapping/src/circuit_input_builder/input_state_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,7 @@ impl<'a> CircuitInputStateRef<'a> {
receiver: Address,
receiver_exists: bool,
must_create: bool,
must_read_caller_balance: bool,
value: Word,
fee: Option<Word>,
is_anchor_tx: bool,
Expand Down Expand Up @@ -534,21 +535,25 @@ impl<'a> CircuitInputStateRef<'a> {
},
)?;
}

// Read the caller balance when required, skip if value == 0 otherwise
if must_read_caller_balance || !value.is_zero() {
self.push_op_reversible(
step,
AccountOp {
address: sender,
field: AccountField::Balance,
value: sender_balance,
value_prev: sender_balance_prev,
},
)?;
}

if value.is_zero() {
// Skip transfer if value == 0
return Ok(());
}

self.push_op_reversible(
step,
AccountOp {
address: sender,
field: AccountField::Balance,
value: sender_balance,
value_prev: sender_balance_prev,
},
)?;

let (_found, receiver_account) = self.sdb.get_account(&receiver);
let receiver_balance_prev = receiver_account.balance;
let receiver_balance = receiver_account.balance + value;
Expand Down Expand Up @@ -581,6 +586,7 @@ impl<'a> CircuitInputStateRef<'a> {
receiver,
receiver_exists,
must_create,
false,
value,
None,
false,
Expand Down
3 changes: 2 additions & 1 deletion bus-mapping/src/circuit_input_builder/tracer_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,14 @@ impl CircuitInputBuilderTx {
let block = crate::mock::BlockData::new_from_geth_data(geth_data.clone());
let mut builder = block.new_circuit_input_builder();
let tx = builder
.new_tx(&block.eth_block.transactions[0], true)
.new_tx(&block.eth_block.transactions[0], true, false)
.unwrap();
let tx_ctx = TransactionContext::new(
&block.eth_block.transactions[0],
&GethExecTrace {
gas: Gas(0),
failed: false,
invalid: false,
return_value: "".to_owned(),
struct_logs: vec![geth_step.clone()],
},
Expand Down
7 changes: 7 additions & 0 deletions bus-mapping/src/circuit_input_builder/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,10 @@ impl TransactionContext {
pub struct Transaction {
/// The raw transaction fields
pub tx: geth_types::Transaction,
/// Invalid tx
pub invalid_tx: bool,
/// AccessListGasCost
pub access_list_gas_cost: u64,
/// Calls made in the transaction
pub(crate) calls: Vec<Call>,
/// Execution steps
Expand All @@ -203,6 +207,7 @@ impl Transaction {
code_db: &mut CodeDB,
eth_tx: &eth_types::Transaction,
is_success: bool,
is_invalid: bool,
) -> Result<Self, Error> {
let (found, _) = sdb.get_account(&eth_tx.from);
if !found {
Expand Down Expand Up @@ -253,6 +258,8 @@ impl Transaction {

Ok(Self {
tx: eth_tx.into(),
invalid_tx: is_invalid,
access_list_gas_cost: 0,
calls: vec![call],
steps: Vec::new(),
})
Expand Down
39 changes: 29 additions & 10 deletions bus-mapping/src/evm/opcodes/begin_end_tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,19 @@ fn gen_begin_tx_steps(state: &mut CircuitInputStateRef) -> Result<ExecStep, Erro
state.call_context_write(&mut exec_step, call.call_id, field, value);
}

// Increase caller's nonce
// Increase caller's nonce when the tx is not invalid
let caller_address = call.caller_address;
let nonce_prev = state.sdb.get_account(&caller_address).1.nonce;
let nonce = if !state.tx.invalid_tx {
nonce_prev + 1
} else {
nonce_prev
};
state.account_write(
&mut exec_step,
caller_address,
AccountField::Nonce,
(nonce_prev + 1).into(),
nonce.into(),
nonce_prev.into(),
)?;

Expand All @@ -76,7 +81,20 @@ fn gen_begin_tx_steps(state: &mut CircuitInputStateRef) -> Result<ExecStep, Erro
} else {
GasCost::TX.as_u64()
} + state.tx.tx.call_data_gas_cost();
exec_step.gas_cost = GasCost(intrinsic_gas_cost);

// Don't pay any fee or transfer any ETH for invalid transactions
let (gas_cost, value, fee) = if state.tx.invalid_tx {
(0, Word::zero(), Some(Word::zero()))
} else {
(
intrinsic_gas_cost,
call.value,
Some(state.tx.tx.gas_price * state.tx.gas()),
)
};

// Set the gas cost
exec_step.gas_cost = GasCost(gas_cost);

// Get code_hash of callee
let (_, callee_account) = state.sdb.get_account(&call.address);
Expand Down Expand Up @@ -104,9 +122,10 @@ fn gen_begin_tx_steps(state: &mut CircuitInputStateRef) -> Result<ExecStep, Erro
call.caller_address,
call.address,
callee_exists,
call.is_create(),
call.value,
Some(state.tx.tx.gas_price * state.tx.gas()),
!state.tx.invalid_tx && call.is_create(),
true,
value,
fee,
state.tx_ctx.is_anchor_tx(),
)?;

Expand All @@ -132,7 +151,7 @@ fn gen_begin_tx_steps(state: &mut CircuitInputStateRef) -> Result<ExecStep, Erro
match (
call.is_create(),
state.is_precompiled(&call.address),
is_empty_code_hash,
is_empty_code_hash || state.tx.invalid_tx,
) {
// 1. Creation transaction.
(true, _, _) => {
Expand Down Expand Up @@ -181,13 +200,13 @@ fn gen_begin_tx_steps(state: &mut CircuitInputStateRef) -> Result<ExecStep, Erro
evm_unimplemented!("Call to precompiled is left unimplemented");
Ok(exec_step)
}
(_, _, is_empty_code_hash) => {
(_, _, do_not_run_code) => {
// 3. Call to account with empty code.
if is_empty_code_hash {
if do_not_run_code {
return Ok(exec_step);
}

// 4. Call to account with non-empty code.
// 4. Call to account with non-empty code/invalid tx.
for (field, value) in [
(CallContextField::Depth, call.depth.into()),
(
Expand Down
1 change: 1 addition & 0 deletions bus-mapping/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@
//! return_value: "".to_string(),
//! gas: Gas(block.eth_block.transactions[0].gas.as_u64()),
//! failed: false,
//! invalid: false,
//! struct_logs: geth_steps,
//! };
//!
Expand Down
4 changes: 4 additions & 0 deletions eth-types/src/evm_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ impl GasCost {
pub const CALL_WITH_VALUE: Self = Self(9000);
/// Constant cost for turning empty account into non-empty account
pub const NEW_ACCOUNT: Self = Self(25000);
/// Gas cost of warming up an account with the access list
pub const ACCESS_LIST_ADDRESS: Self = Self(2400);
/// Gas cost of warming up a storage with the access list
pub const ACCESS_LIST_STORAGE: Self = Self(1900);
/// Cost per byte of deploying a new contract
pub const CODE_DEPOSIT_BYTE_COST: Self = Self(200);
/// Denominator of quadratic part of memory expansion gas cost
Expand Down
4 changes: 4 additions & 0 deletions eth-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,9 @@ pub struct GethExecTrace {
pub gas: Gas,
/// True when the transaction has failed.
pub failed: bool,
/// True when the tx could not execute
#[serde(default)]
pub invalid: bool,
/// Return value of execution which is a hex encoded byte array
#[serde(rename = "returnValue")]
pub return_value: String,
Expand Down Expand Up @@ -559,6 +562,7 @@ mod tests {
assert_eq!(
trace,
GethExecTrace {
invalid: false,
gas: Gas(26809),
failed: false,
return_value: "".to_owned(),
Expand Down
16 changes: 15 additions & 1 deletion external-tracer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,20 @@ pub fn trace(config: &TraceConfig) -> Result<Vec<GethExecTrace>, Error> {
},
)?;

let trace = serde_json::from_str(&trace_string).map_err(Error::SerdeError)?;
let trace: Vec<GethExecTrace> =
serde_json::from_str(&trace_string).map_err(Error::SerdeError)?;
// Don't throw only for specific invalid transactions we support.
for trace in trace.iter() {
if trace.invalid
&& !(trace.return_value.starts_with("nonce too low")
|| trace.return_value.starts_with("nonce too high")
|| trace.return_value.starts_with("intrinsic gas too low")
|| trace
.return_value
.starts_with("insufficient funds for gas * price + value"))
{
return Err(Error::TracingError(trace.return_value.clone()));
}
}
Ok(trace)
}
7 changes: 6 additions & 1 deletion geth-utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,9 @@ license = "MIT OR Apache-2.0"
[build-dependencies]
gobuild = "0.1.0-alpha.1"
log = "0.4.14"
env_logger = "0.9"
env_logger = "0.9"

[dev-dependencies]
eth-types = { path = "../eth-types" }
serde = {version = "1.0.130", features = ["derive"] }
serde_json = "1.0.66"
27 changes: 18 additions & 9 deletions geth-utils/gethutil/trace.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
// while replaying a transaction in debug mode as well as transaction
// execution status, the amount of gas used and the return value
type ExecutionResult struct {
Invalid bool `json:"invalid"`
Gas uint64 `json:"gas"`
Failed bool `json:"failed"`
ReturnValue string `json:"returnValue"`
Expand Down Expand Up @@ -221,15 +222,23 @@ func Trace(config TraceConfig) ([]*ExecutionResult, error) {

result, err := core.ApplyMessage(evm, &message, new(core.GasPool).AddGas(message.GasLimit))
if err != nil {
return nil, fmt.Errorf("Failed to apply config.Transactions[%d]: %w", i, err)
}
stateDB.Finalise(true)

executionResults[i] = &ExecutionResult{
Gas: result.UsedGas,
Failed: result.Failed(),
ReturnValue: fmt.Sprintf("%x", result.ReturnData),
StructLogs: FormatLogs(tracer.StructLogs()),
executionResults[i] = &ExecutionResult{
Invalid: true,
Gas: 0,
Failed: false,
ReturnValue: fmt.Sprintf("%v", err),
StructLogs: []StructLogRes{},
}
} else {
stateDB.Finalise(true)

executionResults[i] = &ExecutionResult{
Invalid: false,
Gas: result.UsedGas,
Failed: result.Failed(),
ReturnValue: fmt.Sprintf("%x", result.ReturnData),
StructLogs: FormatLogs(tracer.StructLogs()),
}
}
}

Expand Down
21 changes: 19 additions & 2 deletions geth-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ impl Display for Error {
#[cfg(test)]
mod test {
use crate::trace;
use eth_types::{Error, GethExecTrace};

#[test]
fn valid_tx() {
Expand Down Expand Up @@ -102,7 +103,15 @@ mod test {
]
}"#,
] {
assert!(trace(config).is_ok());
let trace_result = trace(config);
assert!(trace_result.is_ok());
let trace_string = trace_result.unwrap();
let trace: Vec<GethExecTrace> = serde_json::from_str(&trace_string)
.map_err(Error::SerdeError)
.unwrap();
for trace in trace.iter() {
assert!(!trace.invalid);
}
}
}

Expand Down Expand Up @@ -150,7 +159,15 @@ mod test {
]
}"#,
] {
assert!(trace(config).is_err())
let trace_result = trace(config);
assert!(trace_result.is_ok());
let trace_string = trace_result.unwrap();
let trace: Vec<GethExecTrace> = serde_json::from_str(&trace_string)
.map_err(Error::SerdeError)
.unwrap();
for trace in trace.iter() {
assert!(trace.invalid);
}
}
}
}
Loading
Loading