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

Commit

Permalink
Merge remote-tracking branch 'origin/main' into fix/call-op
Browse files Browse the repository at this point in the history
  • Loading branch information
lispc committed Sep 10, 2023
2 parents 8d0c889 + 588b8b8 commit 6a62e82
Show file tree
Hide file tree
Showing 74 changed files with 16,744 additions and 74 deletions.
10 changes: 4 additions & 6 deletions bus-mapping/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ pub enum OogError {
SloadSstore,
/// Out of Gas for CALL, CALLCODE, DELEGATECALL and STATICCALL
Call,
/// Out of Gas for CREATE2
Create2,
/// Out of Gas for CREATE and CREATE2
Create,
/// Out of Gas for SELFDESTRUCT
SelfDestruct,
}
Expand All @@ -105,9 +105,7 @@ impl From<&OpcodeId> for OogError {
OpcodeId::MLOAD | OpcodeId::MSTORE | OpcodeId::MSTORE8 => {
OogError::StaticMemoryExpansion
}
OpcodeId::CREATE | OpcodeId::RETURN | OpcodeId::REVERT => {
OogError::DynamicMemoryExpansion
}
OpcodeId::RETURN | OpcodeId::REVERT => OogError::DynamicMemoryExpansion,
OpcodeId::CALLDATACOPY
| OpcodeId::CODECOPY
| OpcodeId::EXTCODECOPY
Expand All @@ -124,7 +122,7 @@ impl From<&OpcodeId> for OogError {
OogError::Call
}
OpcodeId::SLOAD | OpcodeId::SSTORE => OogError::SloadSstore,
OpcodeId::CREATE2 => OogError::Create2,
OpcodeId::CREATE | OpcodeId::CREATE2 => OogError::Create,
OpcodeId::SELFDESTRUCT => OogError::SelfDestruct,
_ => OogError::Constant,
}
Expand Down
12 changes: 10 additions & 2 deletions bus-mapping/src/evm/opcodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,12 +270,20 @@ fn fn_gen_associated_ops(opcode_id: &OpcodeId) -> FnGenAssociatedOps {
}
}

fn fn_gen_error_state_associated_ops(error: &ExecError) -> Option<FnGenAssociatedOps> {
fn fn_gen_error_state_associated_ops(
geth_step: &GethExecStep,
error: &ExecError,
) -> Option<FnGenAssociatedOps> {
match error {
ExecError::InvalidJump => Some(InvalidJump::gen_associated_ops),
ExecError::InvalidOpcode => Some(ErrorSimple::gen_associated_ops),
ExecError::OutOfGas(OogError::Call) => Some(OOGCall::gen_associated_ops),
ExecError::OutOfGas(OogError::Constant) => Some(ErrorSimple::gen_associated_ops),
ExecError::OutOfGas(OogError::Create) => match geth_step.op {
OpcodeId::CREATE => Some(StackOnlyOpcode::<3, 0, true>::gen_associated_ops),
OpcodeId::CREATE2 => Some(StackOnlyOpcode::<4, 0, true>::gen_associated_ops),
op => unreachable!("OOG Create cannot occur in {op}"),
},
ExecError::OutOfGas(OogError::Exp) => Some(OOGExp::gen_associated_ops),
ExecError::OutOfGas(OogError::Log) => Some(ErrorOOGLog::gen_associated_ops),
ExecError::OutOfGas(OogError::MemoryCopy) => Some(OOGMemoryCopy::gen_associated_ops),
Expand Down Expand Up @@ -370,7 +378,7 @@ pub fn gen_associated_ops(
// TODO: after more error state handled, refactor all error handling in
// fn_gen_error_state_associated_ops method
// For exceptions that have been implemented
if let Some(fn_gen_error_ops) = fn_gen_error_state_associated_ops(&exec_error) {
if let Some(fn_gen_error_ops) = fn_gen_error_state_associated_ops(geth_step, &exec_error) {
return fn_gen_error_ops(state, geth_steps);
} else {
// For exceptions that fail to enter next call context, we need
Expand Down
26 changes: 16 additions & 10 deletions bus-mapping/src/evm/opcodes/callop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ impl<const N_ARGS: usize> Opcode for CallOpcode<N_ARGS> {
)?;

let callee_code_hash = call.code_hash;
let callee_exists = !state.sdb.get_account(&callee_address).1.is_empty();
let callee = state.sdb.get_account(&callee_address).1.clone();
let callee_exists = !callee.is_empty();

let (callee_code_hash_word, is_empty_code_hash) = if callee_exists {
(
Expand Down Expand Up @@ -145,11 +146,13 @@ impl<const N_ARGS: usize> Opcode for CallOpcode<N_ARGS> {
debug_assert!(found);

let caller_balance = sender_account.balance;

let is_call_or_callcode = call.kind == CallKind::Call || call.kind == CallKind::CallCode;
let is_sufficient = caller_balance >= call.value;
let is_valid_depth = geth_step.depth < 1025;

// Precheck is OK when depth is in range and caller balance is sufficient
let is_precheck_ok =
geth_step.depth < 1025 && (!is_call_or_callcode || caller_balance >= call.value);
let is_precheck_ok = is_valid_depth && (is_sufficient || !is_call_or_callcode);

log::debug!(
"is_precheck_ok: {}, call type: {:?}, sender_account: {:?} ",
Expand All @@ -174,8 +177,11 @@ impl<const N_ARGS: usize> Opcode for CallOpcode<N_ARGS> {
.map(|ref addr| is_precompiled(addr))
.unwrap_or(false);
// CALLCODE does not need to do real transfer.
// Transfer value only for CALL opcode, is_precheck_ok = true.
if call.kind == CallKind::Call && is_precheck_ok {
// Transfer value only when all these conditions met:
// - The opcode is CALL
// - The precheck passed
// - The value to send is not zero
if call.kind == CallKind::Call && is_precheck_ok && !call.value.is_zero() {
state.transfer(
&mut exec_step,
call.caller_address,
Expand Down Expand Up @@ -221,9 +227,9 @@ impl<const N_ARGS: usize> Opcode for CallOpcode<N_ARGS> {

// There are 4 branches from here.
// add failure case for insufficient balance or error depth in the future.
match (!is_precheck_ok, is_precompile, is_empty_code_hash) {
match (is_precheck_ok, is_precompile, is_empty_code_hash) {
// 1. Call to precompiled.
(false, true, _) => {
(true, true, _) => {
let caller_ctx = state.caller_ctx_mut()?;
let code_address = code_address.unwrap();
let (result, contract_gas_cost) = execute_precompiled(
Expand Down Expand Up @@ -274,7 +280,7 @@ impl<const N_ARGS: usize> Opcode for CallOpcode<N_ARGS> {
Ok(vec![exec_step])
}
// 2. Call to account with empty code.
(false, _, true) => {
(true, _, true) => {
for (field, value) in [
(CallContextField::LastCalleeId, call.call_id.into()),
(CallContextField::LastCalleeReturnDataOffset, 0.into()),
Expand All @@ -286,7 +292,7 @@ impl<const N_ARGS: usize> Opcode for CallOpcode<N_ARGS> {
Ok(vec![exec_step])
}
// 3. Call to account with non-empty code.
(false, _, false) => {
(true, _, false) => {
for (field, value) in [
(CallContextField::ProgramCounter, (geth_step.pc + 1).into()),
(
Expand Down Expand Up @@ -348,7 +354,7 @@ impl<const N_ARGS: usize> Opcode for CallOpcode<N_ARGS> {
}

// 4. insufficient balance or error depth cases.
(true, _, _) => {
(false, _, _) => {
for (field, value) in [
(CallContextField::LastCalleeId, call.call_id.into()),
(CallContextField::LastCalleeReturnDataOffset, 0.into()),
Expand Down
33 changes: 33 additions & 0 deletions eth-types/src/evm_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,39 @@ pub const GAS_STIPEND_CALL_WITH_VALUE: u64 = 2300;
/// <https://github.com/ethereum/go-ethereum/blob/e6b6a8b738069ad0579f6798ee59fde93ed13b43/core/vm/gas_table.go#L38>
pub const MAX_EXPANDED_MEMORY_ADDRESS: u64 = 0x1FFFFFFFE0;

#[cfg(feature = "shanghai")]
mod gas_create {
// For EIP-3860, there are 2 special gas cost constraints in geth
// [gasCreate2Eip3860](https://github.com/ethereum/go-ethereum/blob/eb83e7c54021573eaceb14236af3a7a8c64f6027/core/vm/gas_table.go#L321)
// (similar for CREATE).
// 1. size <= 49152 (MaxInitCodeSize)
// 2. gasCost = memoryGasCost + (2 + 6) * ((size + 31) / 32) should not
// overflow for Uint64.
// No need to constrain the second condition, since the maximum gas cost
// cannot overflow for Uint64 (36028809887100925 calculated by
// `memorySize = 0x1FFFFFFFE0` and `size = 49152`) if the first condition is
// satisfied.

/// Maximum init code size to permit in a creation transaction and create instructions.
pub const MAX_INIT_CODE_SIZE: u64 = 2 * super::MAX_CODE_SIZE;
/// Once per word of the init code when creating a contract.
pub const INIT_CODE_WORD_GAS: u64 = 2;
/// Gas per code word for CREATE.
pub const CREATE_GAS_PER_CODE_WORD: u64 = INIT_CODE_WORD_GAS;
/// Gas per code word for CREATE2.
pub const CREATE2_GAS_PER_CODE_WORD: u64 = INIT_CODE_WORD_GAS + super::GasCost::COPY_SHA3.0;
}
#[cfg(not(feature = "shanghai"))]
mod gas_create {
/// Maximum init code size (0x1FFFFFFFE0) if not EIP-3860.
pub use super::MAX_EXPANDED_MEMORY_ADDRESS as MAX_INIT_CODE_SIZE;
/// Gas per code word for CREATE if not EIP-3860.
pub const CREATE_GAS_PER_CODE_WORD: u64 = 0;
/// Gas per code word for CREATE2 if not EIP-3860.
pub const CREATE2_GAS_PER_CODE_WORD: u64 = super::GasCost::COPY_SHA3;
}
pub use gas_create::*;

/// Defines the gas consumption.
pub struct GasCost;

Expand Down
33 changes: 33 additions & 0 deletions mpt-witness-generator/.github/workflows/go.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# This workflow will build a golang project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go

name: Go

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

jobs:

build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.20'

- name: Format
run: go fmt ./...

- name: Build
run: go build -v ./...

- name: Test
env:
NO_GETH: true
run: go test -v ./...
7 changes: 7 additions & 0 deletions mpt-witness-generator/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
rust_call/target
.DS_Store
.vscode
mpt-witness-generator
mpt
generated_witnesses
rust_call/proof.json
124 changes: 124 additions & 0 deletions mpt-witness-generator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# Merkle Patricia Trie witness generator

This project aims to prepare witness generator for Merkle Patricia Trie circuit which is part of
[zkevm-circuits](https://github.com/appliedzkp/zkevm-circuits).

It is based on [geth](https://github.com/ethereum/go-ethereum).
It takes `eth_getProof` output and it transforms it into the MPT circuit witness.

MPT circuit checks that the modification of the trie state happened correctly.

Let us assume there are two proofs (as returned by `eth getProof`):

- A proof that there exists value `val1` at key `key1` for address `addr` in the state trie with root `root1`.
- A proof that there exists value `val2` at key `key1` for address `addr` in the state trie with root `root2`.

The circuit checks the transition from `val1` to `val2` at `key1` that led to the change
of trie root from `root1` to `root2`.

For this reason, there are two parallel proofs for each trie modification.
There is `S` (as `State`) proof which presents the state of the trie
before the modification. And there is `C` (as `Change`) proof which presents the state
of the trie after modification.

An example of `eth_getProof` output is given below:

```
[248 81 128 128 128 160 32 34 39 131 73 65 47 37 211 142 206 231 172 16 11 203 33 107 30 7 213 226 2 174 55 216 4 117 220 10 186 68 128 128 128 128 128 128 128 160 55 235 85 86 230 197 53 159 28 141 120 87 82 57 4 132 185 12 24 158 142 210 106 188 12 87 179 231 52 16 126 229 128 128 128 128 128]
[226 160 59 138 106 70 105 186 37 13 38 205 122 69 158 202 157 33 95 131 7 227 58 235 229 3 121 188 90 54 23 236 52 68 1]
```

The first element of the proof is a branch containing two children. The second element of the proof is
a storage leaf contained in the branch (from the first proof element).

To simplify the MPT circuit, the `eth_getProof` elements are not directly used as rows.
Instead, the rows are prepared as:

```
[1 0 1 0 248 81 0 248 81 0 11 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 128 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 128 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1]
[0 0 128 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 128 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1]
[0 0 128 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 128 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1]
[0 160 32 34 39 131 73 65 47 37 211 142 206 231 172 16 11 203 33 107 30 7 213 226 2 174 55 216 4 117 220 10 186 68 0 160 32 34 39 131 73 65 47 37 211 142 206 231 172 16 11 203 33 107 30 7 213 226 2 174 55 216 4 117 220 10 186 68 1]
[0 0 128 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 128 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1]
[0 0 128 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 128 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1]
[0 0 128 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 128 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1]
[0 0 128 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 128 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1]
[0 0 128 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 128 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1]
[0 0 128 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 128 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1]
[0 0 128 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 128 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1]
[0 160 55 235 85 86 230 197 53 159 28 141 120 87 82 57 4 132 185 12 24 158 142 210 106 188 12 87 179 231 52 16 126 229 0 160 88 197 127 237 244 146 28 57 104 36 96 69 159 84 254 170 28 196 41 183 253 107 213 32 170 141 111 191 30 100 117 55 1]
[0 0 128 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 128 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1]
[0 0 128 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 128 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1]
[0 0 128 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 128 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1]
[0 0 128 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 128 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 16]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 17]
[226 160 59 138 106 70 105 186 37 13 38 205 122 69 158 202 157 33 95 131 7 227 58 235 229 3 121 188 90 54 23 236 52 68 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2]
[1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 13]
[226 160 59 138 106 70 105 186 37 13 38 205 122 69 158 202 157 33 95 131 7 227 58 235 229 3 121 188 90 54 23 236 52 68 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3]
[17 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 15]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 19]
```

...

<!--
What is changed compared to geth:
* Some statedb and trie methods are made public to enable accessing internal structures.
-->

## Generate witnesses

To generate witnesses for MPT circuit, go into witness folder and execute

```
go test gen_witness_from_infura_blockchain_test.go prepare_witness.go leaf.go extension_node.go modified_extension_node.go nodes.go test_tools.go branch.go util.go
```

to generate the tests that use Infura blockchain.

To generate the tests that use a local blockchain you need a local `geth`. You would
need to run something like:
```
geth --dev --http --ipcpath ~/Library/Ethereum/geth.ipc
```
The local `geth` is used to generate some tests that have a small number of accounts so that
these accounts appear in the first or second level of the trie. You might need to remove the
database if you already have some accounts:

```
geth removedb
```

And to generate the tests:

```
go test gen_witness_from_local_blockchain_test.go prepare_witness.go leaf.go extension_node.go modified_extension_node.go nodes.go test_tools.go branch.go util.go
```

The witness files will appear in generated_witnesses folder.

## Calling from Rust

Build:

```
go build -buildmode=c-archive -o libmpt.a witness_gen_wrapper.go
```

Copy libmpt.a and libmpt.h to rust_call/build:

```
mv libmpt.* rust_call/build
```

Note: to avoid the problem described [](https://github.com/golang/go/issues/42459),
the following has been set in rust_call/.cargo/config:

```
[build]
rustflags = ["-C", "link-args=-framework CoreFoundation -framework Security"]
```
10 changes: 10 additions & 0 deletions mpt-witness-generator/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module github.com/privacy-scaling-explorations/mpt-witness-generator

go 1.16

require (
github.com/VictoriaMetrics/fastcache v1.6.0 // indirect
github.com/ethereum/go-ethereum v1.10.8 // indirect
github.com/holiman/uint256 v1.2.0 // indirect
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect
)
Loading

0 comments on commit 6a62e82

Please sign in to comment.