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

Commit

Permalink
Contract creation via Tx (#433)
Browse files Browse the repository at this point in the history
  • Loading branch information
adria0 committed Jun 30, 2023
1 parent e00ffa2 commit 62e894a
Show file tree
Hide file tree
Showing 2 changed files with 263 additions and 21 deletions.
108 changes: 99 additions & 9 deletions src/zkevm_specs/evm_circuit/execution/begin_tx.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from ..execution_state import ExecutionState
from ..instruction import Instruction, Transition
from ..precompile import Precompile
from ..table import CallContextFieldTag, TxContextFieldTag, AccountFieldTag
from ..table import CallContextFieldTag, TxContextFieldTag, AccountFieldTag, CopyDataTypeTag


def begin_tx(instruction: Instruction):
Expand Down Expand Up @@ -41,6 +41,13 @@ def begin_tx(instruction: Instruction):
instruction.constrain_not_zero(tx_caller_address)

# Verify nonce
# TODO: Document that the TxInvalid feature is required to add invalid Tx
# to a block. In regular Ethereum this is not possible because such Txs
# are rejected and never included in a Block. But in a zkRollup setting,
# where the queueing of Txs is decoupled from block formation, there's a
# chance that a Tx is scheduled to be included in a block but it's invalid,
# so the circuit must prove that the Tx in that block is invalid.

is_tx_invalid = instruction.tx_context_lookup(tx_id, TxContextFieldTag.TxInvalid)
tx_nonce = instruction.tx_context_lookup(tx_id, TxContextFieldTag.Nonce)
nonce, nonce_prev = instruction.account_write(tx_caller_address, AccountFieldTag.Nonce)
Expand Down Expand Up @@ -71,14 +78,20 @@ def begin_tx(instruction: Instruction):
gas_not_enough, _ = instruction.compare(tx_gas, tx_intrinsic_gas, MAX_N_BYTES)
gas_left = tx_gas.expr() if gas_not_enough == 1 else tx_gas.expr() - tx_intrinsic_gas

# Calculate new contract address if tx_is_create
contract_address = instruction.generate_contract_address(tx_caller_address, tx_nonce)
contract_address_word = instruction.address_to_word(contract_address)

callee_address = contract_address if tx_is_create == 1 else tx_callee_address

# Prepare access list of caller and callee
instruction.constrain_zero(instruction.add_account_to_access_list(tx_id, tx_caller_address))
instruction.constrain_zero(instruction.add_account_to_access_list(tx_id, tx_callee_address))
instruction.constrain_zero(instruction.add_account_to_access_list(tx_id, callee_address))

# Verify transfer
sender_balance_pair, _ = instruction.transfer_with_gas_fee(
tx_caller_address,
tx_callee_address,
callee_address,
Word(0) if (is_tx_invalid.expr() == 1) else tx_value,
Word(0) if (is_tx_invalid.expr() == 1) else gas_fee,
reversion_info,
Expand All @@ -91,16 +104,93 @@ def begin_tx(instruction: Instruction):
MAX_N_BYTES,
)
invalid_tx = 1 - (1 - balance_not_enough) * (1 - gas_not_enough) * (is_nonce_valid)

# prover should not give incorrect is_tx_invalid flag.
instruction.constrain_equal(is_tx_invalid, invalid_tx)

if tx_is_create == 1:
# TODO: Verify created address
# code_hash represents the contract creation code
# 1. In the case of root call, code_hash is the hash of the tx calldata.
# 2. In the case of internal call, code_hash is the hash of the chunk of
# caller memory corresponding to the contract init code.
raise NotImplementedError
if is_tx_invalid == FQ(1) or tx_call_data_length == 0:
# Make sure tx is persistent
instruction.constrain_equal(reversion_info.is_persistent, FQ(1))

# Do step state transition
instruction.constrain_equal(instruction.next.execution_state, ExecutionState.EndTx)
instruction.constrain_step_state_transition(
rw_counter=Transition.delta(9), call_id=Transition.to(call_id)
)
else:
# Expected behabeur
# - If initcode does not RETRUN, contract is created empty and value transferred
# - If initcode is invalid bytecode or reverts, contract is not created and value not transferred

# Get code hash of tx calldata

copy_rwc_inc, tx_calldata_rlc = instruction.copy_lookup(
tx_id, # src_id
CopyDataTypeTag.TxCalldata, # src_type
call_id, # dst_id
CopyDataTypeTag.RlcAcc, # dst_type
FQ.zero(), # src_addr
tx_call_data_length, # src_addr_boundary
FQ.zero(), # dst_addr
tx_call_data_length, # length
instruction.curr.rw_counter + instruction.rw_counter_offset,
)

assert copy_rwc_inc == FQ.zero()
# no memory involved, no rw counter incremented

code_hash = instruction.keccak_lookup(tx_call_data_length, tx_calldata_rlc)

# Copy tx calldata to bytecode table

copy_rwc_inc, _ = instruction.copy_lookup(
tx_id, # src_id
CopyDataTypeTag.TxCalldata, # src_type
code_hash, # dst_id
CopyDataTypeTag.Bytecode, # dst_type
FQ.zero(), # src_addr
tx_call_data_length, # src_addr_boundary
FQ(0), # dst_addr
tx_call_data_length, # length
instruction.curr.rw_counter + instruction.rw_counter_offset,
)
assert copy_rwc_inc == FQ.zero()
# no memory involved, no rw counter incremented

# Setup next call's context

for tag, word_or_value in [
(CallContextFieldTag.Depth, FQ(1)),
(CallContextFieldTag.CallerAddress, tx_caller_address_word),
(CallContextFieldTag.CalleeAddress, contract_address_word),
(CallContextFieldTag.CallDataOffset, FQ(0)),
(CallContextFieldTag.CallDataLength, tx_call_data_length),
(CallContextFieldTag.Value, tx_value),
(CallContextFieldTag.IsStatic, FQ(False)),
(CallContextFieldTag.LastCalleeId, FQ(0)),
(CallContextFieldTag.LastCalleeReturnDataOffset, FQ(0)),
(CallContextFieldTag.LastCalleeReturnDataLength, FQ(0)),
(CallContextFieldTag.IsRoot, FQ(True)),
(CallContextFieldTag.IsCreate, FQ(True)),
(CallContextFieldTag.CodeHash, code_hash),
]:
instruction.constrain_equal_word(
instruction.call_context_lookup_word(tag, call_id=call_id),
WordOrValue(word_or_value),
)

instruction.step_state_transition_to_new_context(
rw_counter=Transition.delta(22),
call_id=Transition.to(call_id),
is_root=Transition.to(True),
is_create=Transition.to(True),
code_hash=Transition.to_word(code_hash),
gas_left=Transition.to(gas_left),
reversible_write_counter=Transition.to(2),
log_id=Transition.to(0),
)

elif tx_callee_address in list(Precompile):
# TODO: Handle precompile
raise NotImplementedError
Expand Down
Loading

0 comments on commit 62e894a

Please sign in to comment.