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

Commit

Permalink
circuit for invalid creation code (#1292)
Browse files Browse the repository at this point in the history
### Description

circuit for invalid creation code error in create, markdown spec
privacy-scaling-explorations/zkevm-specs#400
### Issue Link

issue #1291 

### Type of change

- [x] New feature (non-breaking change which adds functionality)

### Rationale

Get first byte of code to store , check it is 0xef

This PR contains:
- buss mapping: at the return opcode in create, get the first byte .
- add circuit & test 
- add tx deploy trace test .

---------

Co-authored-by: Steven Gu <[email protected]>
  • Loading branch information
DreamWuGit and silathdiir authored Jul 7, 2023
1 parent a884ae0 commit 3733326
Show file tree
Hide file tree
Showing 11 changed files with 446 additions and 53 deletions.
7 changes: 4 additions & 3 deletions bus-mapping/src/circuit_input_builder/input_state_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1064,9 +1064,10 @@ impl<'a> CircuitInputStateRef<'a> {
let geth_step = steps
.get(0)
.ok_or(Error::InternalError("invalid index 0"))?;
let is_return_revert = geth_step.op == OpcodeId::REVERT || geth_step.op == OpcodeId::RETURN;
let is_revert_or_return_call_success = geth_step.op == OpcodeId::REVERT
|| geth_step.op == OpcodeId::RETURN && exec_step.error.is_none();

if !is_return_revert && !call.is_success {
if !is_revert_or_return_call_success && !call.is_success {
// add call failure ops for exception cases
self.call_context_read(
exec_step,
Expand Down Expand Up @@ -1138,7 +1139,7 @@ impl<'a> CircuitInputStateRef<'a> {
};
let gas_refund = geth_step.gas - memory_expansion_gas_cost - code_deposit_cost;

let caller_gas_left = if is_return_revert || call.is_success {
let caller_gas_left = if is_revert_or_return_call_success || call.is_success {
geth_step_next.gas - gas_refund
} else {
geth_step_next.gas
Expand Down
61 changes: 58 additions & 3 deletions bus-mapping/src/circuit_input_builder/tracer_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::{
};
use eth_types::{
address, bytecode,
evm_types::{stack::Stack, OpcodeId},
evm_types::{stack::Stack, OpcodeId, INVALID_INIT_CODE_FIRST_BYTE},
geth_types::GethData,
word, Bytecode, Hash, ToAddress, ToWord, Word,
};
Expand Down Expand Up @@ -742,11 +742,11 @@ fn check_err_invalid_code(step: &GethExecStep, next_step: Option<&GethExecStep>)
&& result(next_step).is_zero()
&& length > Word::zero()
&& !step.memory.is_empty()
&& step.memory.0.get(offset.low_u64() as usize) == Some(&0xef)
&& step.memory.0.get(offset.low_u64() as usize) == Some(&INVALID_INIT_CODE_FIRST_BYTE)
}

#[test]
fn tracer_err_invalid_code() {
fn tracer_err_invalid_code_for_create_opcode() {
// code_creator outputs byte array that starts with 0xef, which is
// invalid code.
let code_creator = bytecode! {
Expand Down Expand Up @@ -833,6 +833,61 @@ fn tracer_err_invalid_code() {
);
}

// Test ErrInvalidCode in transaction deployment (`tx.to == null`).
#[test]
fn tracer_err_invalid_code_for_tx_deployment() {
// Code creator outputs a byte array that starts with 0xef, which is the
// invalid code.
let code_creator = bytecode! {
PUSH32(word!("0xef00000000000000000000000000000000000000000000000000000000000000")) // value
PUSH1(0x00) // offset
MSTORE
PUSH1(0x01) // length
PUSH1(0x00) // offset
RETURN
};

// Get the execution steps from external tracer.
let block: GethData = TestContext::<2, 1>::new_with_logger_config(
None,
|accs| {
accs[0].address(address!("0x0000000000000000000000000000000000000000"));
accs[1].address(*ADDR_B).balance(Word::from(1u64 << 30));
},
|mut txs, accs| {
txs[0]
.from(accs[1].address)
.gas(60000u64.into())
.input(code_creator.into());
},
|block, _tx| block.number(0x0264),
LoggerConfig::enable_memory(),
)
.unwrap()
.into();

// Get last RETURN.
let (index, step) = block.geth_traces[0]
.struct_logs
.iter()
.enumerate()
.rev()
.find(|(_, s)| s.op == OpcodeId::RETURN)
.unwrap();
let next_step = block.geth_traces[0].struct_logs.get(index + 1);
assert!(check_err_invalid_code(step, next_step));

// Setup call context at RETURN.
let mut builder = CircuitInputBuilderTx::new(&block, step);
builder.tx_ctx.call_is_success.push(false);
builder.state_ref().push_call(mock_root_create());
builder.state_ref().call_ctx_mut().unwrap().memory = step.memory.clone();
assert_eq!(
builder.state_ref().get_step_err(step, next_step).unwrap(),
Some(ExecError::InvalidCreationCode)
);
}

fn check_err_max_code_size_exceeded(step: &GethExecStep, next_step: Option<&GethExecStep>) -> bool {
let length = step.stack.nth_last(1).unwrap();
step.op == OpcodeId::RETURN
Expand Down
3 changes: 3 additions & 0 deletions bus-mapping/src/evm/opcodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ mod stackonlyop;
mod stop;
mod swap;

mod error_invalid_creation_code;
mod error_invalid_jump;
mod error_oog_call;
mod error_oog_exp;
Expand Down Expand Up @@ -71,6 +72,7 @@ use codecopy::Codecopy;
use codesize::Codesize;
use create::Create;
use dup::Dup;
use error_invalid_creation_code::ErrorCreationCode;
use error_invalid_jump::InvalidJump;
use error_oog_call::OOGCall;
use error_oog_exp::OOGExp;
Expand Down Expand Up @@ -297,6 +299,7 @@ fn fn_gen_error_state_associated_ops(error: &ExecError) -> Option<FnGenAssociate
}
ExecError::WriteProtection => Some(ErrorWriteProtection::gen_associated_ops),
ExecError::ReturnDataOutOfBounds => Some(ErrorReturnDataOutOfBound::gen_associated_ops),
ExecError::InvalidCreationCode => Some(ErrorCreationCode::gen_associated_ops),
// call, callcode, create & create2 can encounter DepthError error,
ExecError::Depth(DepthError::Call) => Some(CallOpcode::<7>::gen_associated_ops),
ExecError::Depth(DepthError::Create) => Some(Create::<false>::gen_associated_ops),
Expand Down
37 changes: 37 additions & 0 deletions bus-mapping/src/evm/opcodes/error_invalid_creation_code.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use crate::{
circuit_input_builder::{CircuitInputStateRef, ExecStep},
error::ExecError,
evm::Opcode,
Error,
};
use eth_types::{evm_types::INVALID_INIT_CODE_FIRST_BYTE, GethExecStep};

#[derive(Clone, Copy, Debug)]
pub struct ErrorCreationCode;

impl Opcode for ErrorCreationCode {
fn gen_associated_ops(
state: &mut CircuitInputStateRef,
geth_steps: &[GethExecStep],
) -> Result<Vec<ExecStep>, Error> {
let geth_step = &geth_steps[0];
let mut exec_step = state.new_step(geth_step)?;
exec_step.error = Some(ExecError::InvalidCreationCode);

let offset = geth_step.stack.nth_last(0)?;
let length = geth_step.stack.nth_last(1)?;
state.stack_read(&mut exec_step, geth_step.stack.nth_last_filled(0), offset)?;
state.stack_read(&mut exec_step, geth_step.stack.nth_last_filled(1), length)?;

let call = state.call()?;
assert!(call.is_create() && !length.is_zero());

// Read the first byte of init code and check it must be 0xef for this error.
let init_code_first_byte = state.call_ctx()?.memory.0[offset.as_usize()];
state.memory_read(&mut exec_step, offset.try_into()?, init_code_first_byte)?;
assert_eq!(init_code_first_byte, INVALID_INIT_CODE_FIRST_BYTE);

state.handle_return(&mut exec_step, geth_steps, true)?;
Ok(vec![exec_step])
}
}
7 changes: 6 additions & 1 deletion bus-mapping/src/evm/opcodes/return_revert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{
state_db::CodeDB,
Error,
};
use eth_types::{Bytecode, GethExecStep, ToWord, H256};
use eth_types::{evm_types::INVALID_INIT_CODE_FIRST_BYTE, Bytecode, GethExecStep, ToWord, H256};

#[derive(Debug, Copy, Clone)]
pub(crate) struct ReturnRevert;
Expand Down Expand Up @@ -47,6 +47,11 @@ impl Opcode for ReturnRevert {

// Case A in the spec.
if call.is_create() && call.is_success && length > 0 {
// Read the first byte of init code and check it must not be 0xef (EIP-3541).
let init_code_first_byte = state.call_ctx()?.memory.0[offset];
state.memory_read(&mut exec_step, offset.into(), init_code_first_byte)?;
assert_ne!(init_code_first_byte, INVALID_INIT_CODE_FIRST_BYTE);

// Note: handle_return updates state.code_db. All we need to do here is push the
// copy event.
let code_hash = handle_create(
Expand Down
2 changes: 2 additions & 0 deletions eth-types/src/evm_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ pub use opcode_ids::OpcodeId;
pub use stack::{Stack, StackAddress};
pub use storage::Storage;

/// According to EIP-3541, disallow new code starting with 0xEF to be deployed.
pub const INVALID_INIT_CODE_FIRST_BYTE: u8 = 0xef;
/// Once per word of the init code when creating a contract.
pub const INIT_CODE_WORD_GAS: u64 = 2;
/// Quotient for max refund of gas used
Expand Down
5 changes: 3 additions & 2 deletions zkevm-circuits/src/evm_circuit/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ mod dummy;
mod dup;
mod end_block;
mod end_tx;
mod error_invalid_creation_code;
mod error_invalid_jump;
mod error_invalid_opcode;
mod error_oog_call;
Expand Down Expand Up @@ -139,6 +140,7 @@ use dummy::DummyGadget;
use dup::DupGadget;
use end_block::EndBlockGadget;
use end_tx::EndTxGadget;
use error_invalid_creation_code::ErrorInvalidCreationCodeGadget;
use error_invalid_jump::ErrorInvalidJumpGadget;
use error_invalid_opcode::ErrorInvalidOpcodeGadget;
use error_oog_call::ErrorOOGCallGadget;
Expand Down Expand Up @@ -311,8 +313,7 @@ pub struct ExecutionConfig<F> {
error_depth: Box<DummyGadget<F, 0, 0, { ExecutionState::ErrorDepth }>>,
error_contract_address_collision:
Box<DummyGadget<F, 0, 0, { ExecutionState::ErrorContractAddressCollision }>>,
error_invalid_creation_code:
Box<DummyGadget<F, 0, 0, { ExecutionState::ErrorInvalidCreationCode }>>,
error_invalid_creation_code: Box<ErrorInvalidCreationCodeGadget<F>>,
error_return_data_out_of_bound: Box<ErrorReturnDataOutOfBoundGadget<F>>,
}

Expand Down
Loading

0 comments on commit 3733326

Please sign in to comment.