├── .arcconfig ├── .cargo └── config.toml ├── .editorconfig ├── .github └── workflows │ └── rust.yml ├── .gitignore ├── .gitmodules ├── AUTHORS ├── Cargo.toml ├── LICENSE ├── README.md ├── features └── evm64 │ ├── Cargo.toml │ └── src │ ├── eval.rs │ ├── gasometer.rs │ └── lib.rs ├── interpreter ├── Cargo.toml ├── src │ ├── error │ │ ├── exit.rs │ │ ├── mod.rs │ │ └── trap.rs │ ├── etable.rs │ ├── eval │ │ ├── arithmetic.rs │ │ ├── bitwise.rs │ │ ├── macros.rs │ │ ├── misc.rs │ │ ├── mod.rs │ │ └── system.rs │ ├── interpreter │ │ ├── etable.rs │ │ ├── mod.rs │ │ └── valids.rs │ ├── lib.rs │ ├── machine │ │ ├── memory.rs │ │ ├── mod.rs │ │ └── stack.rs │ ├── opcode.rs │ ├── runtime.rs │ └── utils.rs └── tests │ ├── performance.rs │ └── usability.rs ├── jsontests ├── Cargo.toml └── src │ ├── error.rs │ ├── hash.rs │ ├── in_memory.rs │ ├── lib.rs │ ├── main.rs │ ├── run.rs │ └── types.rs ├── precompile ├── Cargo.toml └── src │ ├── blake2 │ ├── eip152.rs │ └── mod.rs │ ├── bn128.rs │ ├── lib.rs │ ├── modexp.rs │ └── simple.rs ├── rust-toolchain.toml ├── rustfmt.toml ├── src ├── backend │ ├── mod.rs │ └── overlayed.rs ├── call_stack.rs ├── gasometer.rs ├── invoker.rs ├── lib.rs └── standard │ ├── config.rs │ ├── gasometer │ ├── consts.rs │ ├── costs.rs │ ├── mod.rs │ └── utils.rs │ ├── invoker │ ├── mod.rs │ ├── resolver.rs │ ├── routines.rs │ └── state.rs │ └── mod.rs ├── tests ├── contract │ ├── DeployAndDestroy.sol │ ├── SimpleContract.sol │ ├── deploy_and_destroy_init_code │ └── simple_contract_bytecode.txt ├── eip_6780.rs └── mock.rs └── tracer ├── Cargo.toml └── src ├── lib.rs └── standard.rs /.arcconfig: -------------------------------------------------------------------------------- 1 | { 2 | "phabricator.uri" : "https://source.that.world/" 3 | } -------------------------------------------------------------------------------- /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.'cfg(all())'] 2 | rustflags = [ 3 | "-Aclippy::blocks_in_conditions", 4 | ] -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style=tab 5 | indent_size=tab 6 | tab_width=4 7 | end_of_line=lf 8 | charset=utf-8 9 | trim_trailing_whitespace=true 10 | max_line_length=80 11 | insert_final_newline=true 12 | 13 | [*.yml] 14 | indent_style=space 15 | indent_size=2 16 | tab_width=8 17 | end_of_line=lf 18 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ master, next ] 6 | pull_request: 7 | branches: [ master, next ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | lint: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | - name: Rustfmt 18 | run: cargo fmt --all -- --check 19 | - name: clippy 20 | run: cargo clippy --workspace -- -D warnings 21 | - name: clippy no-std 22 | run: cargo clippy --workspace --no-default-features --all-targets -- -D warnings 23 | - name: clippy with features 24 | run: cargo clippy --workspace --all-features --all-targets -- -D warnings 25 | build: 26 | runs-on: ubuntu-latest 27 | steps: 28 | - uses: actions/checkout@v4 29 | - name: Build 30 | run: cargo build --verbose 31 | - name: Build no-std 32 | run: cargo build --no-default-features 33 | - name: Build all-features 34 | run: cargo build --all-features 35 | - name: Run tests 36 | run: cargo test --verbose 37 | jsontests: 38 | runs-on: ubuntu-latest 39 | steps: 40 | - uses: actions/checkout@v4 41 | with: 42 | submodules: recursive 43 | - name: Run tests 44 | run: | 45 | cargo run --release --verbose -p jsontests -- \ 46 | jsontests/res/ethtests/GeneralStateTests/stArgsZeroOneBalance/ \ 47 | jsontests/res/ethtests/GeneralStateTests/stCodeCopyTest/ \ 48 | jsontests/res/ethtests/GeneralStateTests/stExample/ \ 49 | jsontests/res/ethtests/GeneralStateTests/stSelfBalance \ 50 | jsontests/res/ethtests/GeneralStateTests/stSLoadTest/ \ 51 | jsontests/res/ethtests/GeneralStateTests/VMTests/vmArithmeticTest/ \ 52 | jsontests/res/ethtests/GeneralStateTests/VMTests/vmBitwiseLogicOperation/ \ 53 | jsontests/res/ethtests/GeneralStateTests/VMTests/vmIOandFlowOperations/ \ 54 | jsontests/res/ethtests/GeneralStateTests/VMTests/vmLogTest/ \ 55 | jsontests/res/ethtests/GeneralStateTests/VMTests/vmTests/ \ 56 | jsontests/res/ethtests/GeneralStateTests/stEIP150singleCodeGasPrices/eip2929.json 57 | 58 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.so 3 | *.rlib 4 | *.dll 5 | *.swp 6 | *.exe 7 | *.iml 8 | .idea 9 | target 10 | **/result 11 | tests.bin 12 | Cargo.lock 13 | perf.data 14 | perf.data.old 15 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "jsontests/res/ethtests"] 2 | path = jsontests/res/ethtests 3 | url = https://github.com/ethereum/tests 4 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Wei Tang 2 | Parity Technologies 3 | Stewart Mackenzie 4 | Mike Lubinets 5 | Michael Birch 6 | nanocryk 7 | Joshua J. Bouw 8 | tgmichel 9 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "interpreter", 4 | "jsontests", 5 | "precompile", 6 | "tracer", 7 | 8 | "features/evm64", 9 | ] 10 | resolver = "2" 11 | 12 | [workspace.package] 13 | edition = "2021" 14 | rust-version = "1.70.0" 15 | license = "Apache-2.0" 16 | authors = ["rust-evm Developers "] 17 | repository = "https://github.com/rust-ethereum/evm" 18 | keywords = ["no_std", "ethereum", "evm"] 19 | 20 | [package] 21 | name = "evm" 22 | version = "1.0.0-dev" 23 | edition = { workspace = true } 24 | rust-version = { workspace = true } 25 | license = { workspace = true } 26 | authors = { workspace = true } 27 | repository = { workspace = true } 28 | keywords = { workspace = true } 29 | description = "Ethereum Virtual Machine" 30 | 31 | [dependencies] 32 | primitive-types = { version = "0.12", default-features = false, features = ["rlp"] } 33 | sha3 = { version = "0.10", default-features = false } 34 | 35 | evm-interpreter = { version = "1.0.0-dev", path = "interpreter", default-features = false } 36 | 37 | [dev-dependencies] 38 | hex = { version = "0.4", features = [ "serde" ] } 39 | 40 | [features] 41 | default = ["std"] 42 | std = [ 43 | "primitive-types/std", 44 | "sha3/std", 45 | "evm-interpreter/std", 46 | ] 47 | scale = [ 48 | "primitive-types/codec", 49 | "primitive-types/scale-info", 50 | "evm-interpreter/scale", 51 | ] 52 | serde = [ 53 | "primitive-types/impl-serde", 54 | "evm-interpreter/serde", 55 | ] 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust EVM 2 | 3 | [![Build Status](https://github.com/rust-ethereum/evm/workflows/Rust/badge.svg)](https://github.com/rust-ethereum/evm/actions?query=workflow%3ARust) 4 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](./LICENSE) 5 | 6 | Rust EVM, also known as SputnikVM, is a flexible Ethereum Virtual Machine 7 | interpreter that can be easily customized. 8 | 9 | ## Status 10 | 11 | The Rust EVM project has a long history dating back to the initial 12 | implementation in 2017 (when it was called SputnikVM). It has gone through 13 | multiple rewrites over the years to accommodate for different requirements, 14 | when we successfully tested one integrating Geth to sync the mainnet. 15 | 16 | The current rewrite is used in production for the Frontier project (the 17 | Ethereum-compatibility layer for Polkadot). However, we have not yet fully 18 | tested it against Ethereum mainnet. If you have such requirements, PR for fixes 19 | are welcomed. 20 | 21 | ## Features 22 | 23 | * **Standalone** - can be launched as an independent process or integrated into other apps. 24 | * **Flexible** - can be customized and extended to support additional opcodes, 25 | additional precompiles, different gasometers or other more exotic use cases. 26 | * **Portable** - support `no_std`, and can be used in different environments 27 | like in WebAssembly. 28 | * **Fast** - we of course try to be fast! 29 | * written in Rust, can be used as a binary, cargo crate or shared library. 30 | 31 | ## Dependencies 32 | 33 | Rust EVM requires at least `rustc 1.75`. 34 | 35 | ## Documentation 36 | 37 | * [Latest release documentation](https://docs.rs/evm) 38 | 39 | ## License 40 | 41 | Apache 2.0 42 | -------------------------------------------------------------------------------- /features/evm64/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "evm-feature-evm64" 3 | version = "0.0.0-dev" 4 | edition = { workspace = true } 5 | rust-version = { workspace = true } 6 | license = { workspace = true } 7 | authors = { workspace = true } 8 | repository = { workspace = true } 9 | keywords = { workspace = true } 10 | 11 | [dependencies] 12 | primitive-types = { version = "0.12", default-features = false, features = ["rlp"] } 13 | 14 | evm = { path = "../..", default-features = false } 15 | 16 | [features] 17 | default = ["std"] 18 | std = [ 19 | "primitive-types/std", 20 | "evm/std", 21 | ] 22 | -------------------------------------------------------------------------------- /features/evm64/src/eval.rs: -------------------------------------------------------------------------------- 1 | use core::ops::{BitAnd, BitOr, BitXor}; 2 | use primitive_types::U256; 3 | 4 | use evm::interpreter::{error::ExitException, etable::Control, machine::Machine}; 5 | 6 | macro_rules! pop_u64 { 7 | ( $machine:expr, $( $x:ident ),* ) => ( 8 | $( 9 | let $x = match $machine.stack.pop() { 10 | Ok(value) => value.0[0], 11 | Err(e) => return Control::Exit(e.into()), 12 | }; 13 | )* 14 | ); 15 | } 16 | 17 | macro_rules! push_u64 { 18 | ( $machine:expr, $( $x:expr ),* ) => ( 19 | $( 20 | match $machine.stack.push(U256([$x, 0, 0, 0])) { 21 | Ok(()) => (), 22 | Err(e) => return Control::Exit(e.into()), 23 | } 24 | )* 25 | ) 26 | } 27 | 28 | macro_rules! op1_u64_fn { 29 | ($machine:expr, $op:path) => {{ 30 | pop_u64!($machine, op1); 31 | let ret = $op(op1); 32 | push_u64!($machine, ret); 33 | 34 | Control::Continue(1) 35 | }}; 36 | } 37 | 38 | macro_rules! op2_u64_bool_ref { 39 | ($machine:expr, $op:ident) => {{ 40 | pop_u64!($machine, op1, op2); 41 | let ret = op1.$op(&op2); 42 | push_u64!($machine, if ret { 1 } else { 0 }); 43 | 44 | Control::Continue(1) 45 | }}; 46 | } 47 | 48 | macro_rules! op2_u64 { 49 | ($machine:expr, $op:ident) => {{ 50 | pop_u64!($machine, op1, op2); 51 | let ret = op1.$op(op2); 52 | push_u64!($machine, ret); 53 | 54 | Control::Continue(1) 55 | }}; 56 | } 57 | 58 | macro_rules! op2_u64_tuple { 59 | ($machine:expr, $op:ident) => {{ 60 | pop_u64!($machine, op1, op2); 61 | let (ret, ..) = op1.$op(op2); 62 | push_u64!($machine, ret); 63 | 64 | Control::Continue(1) 65 | }}; 66 | } 67 | 68 | macro_rules! op2_u64_fn { 69 | ($machine:expr, $op:path) => {{ 70 | pop_u64!($machine, op1, op2); 71 | let ret = $op(op1, op2); 72 | push_u64!($machine, ret); 73 | 74 | Control::Continue(1) 75 | }}; 76 | } 77 | 78 | macro_rules! op3_u64_fn { 79 | ($machine:expr, $op:path) => {{ 80 | pop_u64!($machine, op1, op2, op3); 81 | let ret = $op(op1, op2, op3); 82 | push_u64!($machine, ret); 83 | 84 | Control::Continue(1) 85 | }}; 86 | } 87 | 88 | pub fn eval_add( 89 | machine: &mut Machine, 90 | _handle: &mut H, 91 | _position: usize, 92 | ) -> Control { 93 | op2_u64_tuple!(machine, overflowing_add) 94 | } 95 | 96 | pub fn eval_mul( 97 | machine: &mut Machine, 98 | _handle: &mut H, 99 | _position: usize, 100 | ) -> Control { 101 | op2_u64_tuple!(machine, overflowing_mul) 102 | } 103 | 104 | pub fn eval_sub( 105 | machine: &mut Machine, 106 | _handle: &mut H, 107 | _position: usize, 108 | ) -> Control { 109 | op2_u64_tuple!(machine, overflowing_sub) 110 | } 111 | 112 | #[inline] 113 | fn div(op1: u64, op2: u64) -> u64 { 114 | if op2 == 0 { 115 | 0 116 | } else { 117 | op1 / op2 118 | } 119 | } 120 | 121 | pub fn eval_div( 122 | machine: &mut Machine, 123 | _handle: &mut H, 124 | _position: usize, 125 | ) -> Control { 126 | op2_u64_fn!(machine, div) 127 | } 128 | 129 | #[inline] 130 | pub fn sdiv(op1: u64, op2: u64) -> u64 { 131 | let op1 = op1 as i64; 132 | let op2 = op2 as i64; 133 | let ret = op1 / op2; 134 | ret as u64 135 | } 136 | 137 | pub fn eval_sdiv( 138 | machine: &mut Machine, 139 | _handle: &mut H, 140 | _position: usize, 141 | ) -> Control { 142 | op2_u64_fn!(machine, sdiv) 143 | } 144 | 145 | #[inline] 146 | pub fn rem(op1: u64, op2: u64) -> u64 { 147 | if op2 == 0 { 148 | 0 149 | } else { 150 | // For unsigned integers overflow never occurs. 151 | op1.overflowing_rem(op2).0 152 | } 153 | } 154 | 155 | pub fn eval_mod( 156 | machine: &mut Machine, 157 | _handle: &mut H, 158 | _position: usize, 159 | ) -> Control { 160 | op2_u64_fn!(machine, rem) 161 | } 162 | 163 | #[inline] 164 | pub fn srem(op1: u64, op2: u64) -> u64 { 165 | if op2 == 0 { 166 | 0 167 | } else { 168 | let op1 = op1 as i64; 169 | let op2 = op2 as i64; 170 | let ret = op1.overflowing_rem(op2).0; 171 | ret as u64 172 | } 173 | } 174 | 175 | pub fn eval_smod( 176 | machine: &mut Machine, 177 | _handle: &mut H, 178 | _position: usize, 179 | ) -> Control { 180 | op2_u64_fn!(machine, srem) 181 | } 182 | 183 | #[inline] 184 | pub fn addmod(op1: u64, op2: u64, op3: u64) -> u64 { 185 | let op1 = op1 as u128; 186 | let op2 = op2 as u128; 187 | let op3 = op3 as u128; 188 | 189 | if op3 == 0 { 190 | 0 191 | } else { 192 | let v = (op1 + op2) % op3; 193 | v as u64 194 | } 195 | } 196 | 197 | pub fn eval_addmod( 198 | machine: &mut Machine, 199 | _handle: &mut H, 200 | _position: usize, 201 | ) -> Control { 202 | op3_u64_fn!(machine, addmod) 203 | } 204 | 205 | #[inline] 206 | pub fn mulmod(op1: u64, op2: u64, op3: u64) -> u64 { 207 | let op1 = op1 as u128; 208 | let op2 = op2 as u128; 209 | let op3 = op3 as u128; 210 | 211 | if op3 == 0 { 212 | 0 213 | } else { 214 | let v = (op1 * op2) % op3; 215 | v as u64 216 | } 217 | } 218 | 219 | pub fn eval_mulmod( 220 | machine: &mut Machine, 221 | _handle: &mut H, 222 | _position: usize, 223 | ) -> Control { 224 | op3_u64_fn!(machine, mulmod) 225 | } 226 | 227 | #[inline] 228 | pub fn exp(op1: u64, op2: u64) -> u64 { 229 | let mut op1 = op1; 230 | let mut op2 = op2; 231 | let mut r = 1u64; 232 | 233 | while op2 != 0 { 234 | if op2 & 1 != 0 { 235 | r = r.overflowing_mul(op1).0; 236 | } 237 | op2 >>= 1; 238 | op1 = op1.overflowing_mul(op1).0; 239 | } 240 | 241 | r 242 | } 243 | 244 | pub fn eval_exp( 245 | machine: &mut Machine, 246 | _handle: &mut H, 247 | _position: usize, 248 | ) -> Control { 249 | op2_u64_fn!(machine, exp) 250 | } 251 | 252 | pub fn eval_lt( 253 | machine: &mut Machine, 254 | _handle: &mut H, 255 | _position: usize, 256 | ) -> Control { 257 | op2_u64_bool_ref!(machine, lt) 258 | } 259 | 260 | pub fn eval_gt( 261 | machine: &mut Machine, 262 | _handle: &mut H, 263 | _position: usize, 264 | ) -> Control { 265 | op2_u64_bool_ref!(machine, gt) 266 | } 267 | 268 | #[inline] 269 | pub fn slt(op1: u64, op2: u64) -> u64 { 270 | let op1 = op1 as i64; 271 | let op2 = op2 as i64; 272 | 273 | if op1.lt(&op2) { 274 | 1 275 | } else { 276 | 0 277 | } 278 | } 279 | 280 | pub fn eval_slt( 281 | machine: &mut Machine, 282 | _handle: &mut H, 283 | _position: usize, 284 | ) -> Control { 285 | op2_u64_fn!(machine, slt) 286 | } 287 | 288 | #[inline] 289 | pub fn sgt(op1: u64, op2: u64) -> u64 { 290 | let op1 = op1 as i64; 291 | let op2 = op2 as i64; 292 | 293 | if op1.gt(&op2) { 294 | 1 295 | } else { 296 | 0 297 | } 298 | } 299 | 300 | pub fn eval_sgt( 301 | machine: &mut Machine, 302 | _handle: &mut H, 303 | _position: usize, 304 | ) -> Control { 305 | op2_u64_fn!(machine, sgt) 306 | } 307 | 308 | pub fn eval_eq( 309 | machine: &mut Machine, 310 | _handle: &mut H, 311 | _position: usize, 312 | ) -> Control { 313 | op2_u64_bool_ref!(machine, eq) 314 | } 315 | 316 | #[inline] 317 | pub fn iszero(op1: u64) -> u64 { 318 | if op1 == 0 { 319 | 1 320 | } else { 321 | 0 322 | } 323 | } 324 | 325 | pub fn eval_iszero( 326 | machine: &mut Machine, 327 | _handle: &mut H, 328 | _position: usize, 329 | ) -> Control { 330 | op1_u64_fn!(machine, iszero) 331 | } 332 | 333 | pub fn eval_and( 334 | machine: &mut Machine, 335 | _handle: &mut H, 336 | _position: usize, 337 | ) -> Control { 338 | op2_u64!(machine, bitand) 339 | } 340 | 341 | pub fn eval_or( 342 | machine: &mut Machine, 343 | _handle: &mut H, 344 | _position: usize, 345 | ) -> Control { 346 | op2_u64!(machine, bitor) 347 | } 348 | 349 | pub fn eval_xor( 350 | machine: &mut Machine, 351 | _handle: &mut H, 352 | _position: usize, 353 | ) -> Control { 354 | op2_u64!(machine, bitxor) 355 | } 356 | 357 | #[inline] 358 | pub fn not(op1: u64) -> u64 { 359 | !op1 360 | } 361 | 362 | pub fn eval_not( 363 | machine: &mut Machine, 364 | _handle: &mut H, 365 | _position: usize, 366 | ) -> Control { 367 | op1_u64_fn!(machine, not) 368 | } 369 | 370 | #[inline] 371 | pub fn shl(shift: u64, value: u64) -> u64 { 372 | if value == 0 || shift >= 64 { 373 | 0 374 | } else { 375 | value << shift as usize 376 | } 377 | } 378 | 379 | pub fn eval_shl( 380 | machine: &mut Machine, 381 | _handle: &mut H, 382 | _position: usize, 383 | ) -> Control { 384 | op2_u64_fn!(machine, shl) 385 | } 386 | 387 | #[inline] 388 | pub fn shr(shift: u64, value: u64) -> u64 { 389 | if value == 0 || shift >= 64 { 390 | 0 391 | } else { 392 | value >> shift as usize 393 | } 394 | } 395 | 396 | pub fn eval_shr( 397 | machine: &mut Machine, 398 | _handle: &mut H, 399 | _position: usize, 400 | ) -> Control { 401 | op2_u64_fn!(machine, shr) 402 | } 403 | 404 | #[inline] 405 | pub fn sar(shift: u64, value: u64) -> u64 { 406 | let value = value as i64; 407 | 408 | let ret = if value == 0 || shift >= 64 { 409 | if value >= 0 { 410 | 0 411 | } else { 412 | -1 413 | } 414 | } else { 415 | value >> shift as usize 416 | }; 417 | 418 | ret as u64 419 | } 420 | 421 | pub fn eval_sar( 422 | machine: &mut Machine, 423 | _handle: &mut H, 424 | _position: usize, 425 | ) -> Control { 426 | op2_u64_fn!(machine, sar) 427 | } 428 | 429 | pub fn eval_jump( 430 | machine: &mut Machine, 431 | _handle: &mut H, 432 | _position: usize, 433 | ) -> Control { 434 | pop_u64!(machine, dest); 435 | 436 | if let Ok(dest) = usize::try_from(dest) { 437 | Control::Jump(dest) 438 | } else { 439 | Control::Exit(ExitException::InvalidJump.into()) 440 | } 441 | } 442 | 443 | pub fn eval_jumpi( 444 | machine: &mut Machine, 445 | _handle: &mut H, 446 | _position: usize, 447 | ) -> Control { 448 | pop_u64!(machine, dest, value); 449 | 450 | if value == 0 { 451 | Control::Continue(1) 452 | } else if let Ok(dest) = usize::try_from(dest) { 453 | Control::Jump(dest) 454 | } else { 455 | Control::Exit(ExitException::InvalidJump.into()) 456 | } 457 | } 458 | -------------------------------------------------------------------------------- /features/evm64/src/gasometer.rs: -------------------------------------------------------------------------------- 1 | use evm::{ 2 | interpreter::{error::ExitException, etable::Control, machine::Machine, opcode::Opcode}, 3 | standard::GasometerState, 4 | }; 5 | 6 | macro_rules! try_or_fail { 7 | ($e:expr) => { 8 | match $e { 9 | Ok(v) => v, 10 | Err(e) => return Control::Exit(e.into()), 11 | } 12 | }; 13 | } 14 | 15 | pub fn eval<'config, S, H, Tr>( 16 | machine: &mut Machine, 17 | _handler: &mut H, 18 | position: usize, 19 | ) -> Control 20 | where 21 | S: AsRef> + AsMut>, 22 | { 23 | const _G_BASE64: u64 = 1; 24 | const G_VERYLOW64: u64 = 2; 25 | const G_LOW64: u64 = 3; 26 | const G_MID64: u64 = 5; 27 | const G_HIGH64: u64 = 7; 28 | const G_EXP64_STATIC: u64 = 5; 29 | const G_EXP64_DYNAMIC: u64 = 25; 30 | 31 | const STATIC_COST_TABLE: [u64; 256] = { 32 | let mut table = [0; 256]; 33 | table[Opcode::ADD.as_usize()] = G_VERYLOW64; 34 | table[Opcode::SUB.as_usize()] = G_VERYLOW64; 35 | table[Opcode::MUL.as_usize()] = G_LOW64; 36 | table[Opcode::DIV.as_usize()] = G_LOW64; 37 | table[Opcode::SDIV.as_usize()] = G_LOW64; 38 | table[Opcode::MOD.as_usize()] = G_LOW64; 39 | table[Opcode::SMOD.as_usize()] = G_LOW64; 40 | table[Opcode::ADDMOD.as_usize()] = G_MID64; 41 | table[Opcode::MULMOD.as_usize()] = G_MID64; 42 | table[Opcode::LT.as_usize()] = G_VERYLOW64; 43 | table[Opcode::GT.as_usize()] = G_VERYLOW64; 44 | table[Opcode::SLT.as_usize()] = G_VERYLOW64; 45 | table[Opcode::SGT.as_usize()] = G_VERYLOW64; 46 | table[Opcode::EQ.as_usize()] = G_VERYLOW64; 47 | table[Opcode::AND.as_usize()] = G_VERYLOW64; 48 | table[Opcode::OR.as_usize()] = G_VERYLOW64; 49 | table[Opcode::XOR.as_usize()] = G_VERYLOW64; 50 | table[Opcode::ISZERO.as_usize()] = G_VERYLOW64; 51 | table[Opcode::NOT.as_usize()] = G_VERYLOW64; 52 | table[Opcode::SHL.as_usize()] = G_VERYLOW64; 53 | table[Opcode::SHR.as_usize()] = G_VERYLOW64; 54 | table[Opcode::SAR.as_usize()] = G_VERYLOW64; 55 | table[Opcode::JUMP.as_usize()] = G_MID64; 56 | table[Opcode::JUMPI.as_usize()] = G_HIGH64; 57 | 58 | table 59 | }; 60 | 61 | let opcode = Opcode(machine.code()[position]); 62 | 63 | try_or_fail!(machine 64 | .state 65 | .as_mut() 66 | .record_gas64(STATIC_COST_TABLE[opcode.as_usize()])); 67 | 68 | #[allow(clippy::single_match)] 69 | match opcode { 70 | Opcode::EXP => { 71 | let power = try_or_fail!(machine.stack.peek(1)).0[0]; 72 | let cost = if power == 0 { 73 | G_EXP64_STATIC 74 | } else { 75 | try_or_fail!(G_EXP64_DYNAMIC 76 | .checked_mul(power.ilog2() as u64 / 8 + 1) 77 | .and_then(|dynamic| G_EXP64_STATIC.checked_add(dynamic)) 78 | .ok_or(ExitException::OutOfGas)) 79 | }; 80 | try_or_fail!(machine.state.as_mut().record_gas64(cost)); 81 | } 82 | _ => (), 83 | } 84 | 85 | Control::NoAction 86 | } 87 | -------------------------------------------------------------------------------- /features/evm64/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # The EVM64 feature 2 | //! 3 | //! See [EIP-7937](https://eips.ethereum.org/EIPS/eip-7937). 4 | 5 | pub mod eval; 6 | mod gasometer; 7 | 8 | pub use crate::gasometer::eval as eval_gasometer; 9 | 10 | use evm::{ 11 | interpreter::{ 12 | etable::{Etable, MultiEfn, MultiEtable, Single}, 13 | opcode::Opcode, 14 | }, 15 | standard::GasometerState, 16 | }; 17 | 18 | pub const OPCODE_EVM64_MODE: Opcode = Opcode(0xc0); 19 | 20 | /// Append a normal `(gasometer, runtime)` etable with EVM64 gasometer and 21 | /// opcodes. 22 | pub fn etable<'config, S, H, Tr>( 23 | orig: (Single, Etable), 24 | ) -> (Etable, MultiEtable) 25 | where 26 | S: AsRef> + AsMut>, 27 | { 28 | let mut gasometer_etable = Etable::from(orig.0); 29 | let mut eval_etable = MultiEtable::from(orig.1); 30 | 31 | let mut mode_etable = Etable::none(); 32 | mode_etable[Opcode::ADD.as_usize()] = eval::eval_add; 33 | mode_etable[Opcode::MUL.as_usize()] = eval::eval_mul; 34 | mode_etable[Opcode::SUB.as_usize()] = eval::eval_sub; 35 | mode_etable[Opcode::DIV.as_usize()] = eval::eval_div; 36 | mode_etable[Opcode::SDIV.as_usize()] = eval::eval_sdiv; 37 | mode_etable[Opcode::MOD.as_usize()] = eval::eval_mod; 38 | mode_etable[Opcode::SMOD.as_usize()] = eval::eval_smod; 39 | mode_etable[Opcode::ADDMOD.as_usize()] = eval::eval_addmod; 40 | mode_etable[Opcode::MULMOD.as_usize()] = eval::eval_mulmod; 41 | mode_etable[Opcode::EXP.as_usize()] = eval::eval_exp; 42 | mode_etable[Opcode::LT.as_usize()] = eval::eval_lt; 43 | mode_etable[Opcode::GT.as_usize()] = eval::eval_gt; 44 | mode_etable[Opcode::SLT.as_usize()] = eval::eval_slt; 45 | mode_etable[Opcode::SGT.as_usize()] = eval::eval_sgt; 46 | mode_etable[Opcode::EQ.as_usize()] = eval::eval_eq; 47 | mode_etable[Opcode::ISZERO.as_usize()] = eval::eval_iszero; 48 | mode_etable[Opcode::AND.as_usize()] = eval::eval_and; 49 | mode_etable[Opcode::OR.as_usize()] = eval::eval_or; 50 | mode_etable[Opcode::XOR.as_usize()] = eval::eval_xor; 51 | mode_etable[Opcode::NOT.as_usize()] = eval::eval_not; 52 | mode_etable[Opcode::SHL.as_usize()] = eval::eval_shl; 53 | mode_etable[Opcode::SHR.as_usize()] = eval::eval_shr; 54 | mode_etable[Opcode::SAR.as_usize()] = eval::eval_sar; 55 | mode_etable[Opcode::JUMP.as_usize()] = eval::eval_jump; 56 | mode_etable[Opcode::JUMPI.as_usize()] = eval::eval_jumpi; 57 | 58 | gasometer_etable[OPCODE_EVM64_MODE.as_usize()] = eval_gasometer; 59 | eval_etable[OPCODE_EVM64_MODE.as_usize()] = MultiEfn::Node(Box::new(mode_etable.into())); 60 | 61 | (gasometer_etable, eval_etable) 62 | } 63 | -------------------------------------------------------------------------------- /interpreter/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "evm-interpreter" 3 | version = "1.0.0-dev" 4 | edition = { workspace = true } 5 | rust-version = { workspace = true } 6 | license = { workspace = true } 7 | authors = { workspace = true } 8 | repository = { workspace = true } 9 | keywords = { workspace = true } 10 | description = "The interpreter part of Ethereum Virtual Machine" 11 | 12 | [dependencies] 13 | auto_impl = "1.2" 14 | paste = "1.0" 15 | primitive-types = { version = "0.12", default-features = false, features = ["rlp"] } 16 | rlp = { version = "0.5", default-features = false } 17 | scale-codec = { package = "parity-scale-codec", version = "3.2", default-features = false, features = ["derive", "full"], optional = true } 18 | scale-info = { version = "2.3", default-features = false, features = ["derive"], optional = true } 19 | serde = { version = "1.0", default-features = false, features = ["derive"], optional = true } 20 | sha3 = { version = "0.10", default-features = false } 21 | 22 | [dev-dependencies] 23 | hex = "0.4" 24 | 25 | [features] 26 | default = ["std"] 27 | std = [ 28 | "primitive-types/std", 29 | "rlp/std", 30 | "scale-codec/std", 31 | "scale-info/std", 32 | "serde/std", 33 | "sha3/std", 34 | ] 35 | scale = [ 36 | "scale-codec", 37 | "scale-info", 38 | "primitive-types/impl-codec", 39 | ] 40 | serde = [ 41 | "dep:serde", 42 | "primitive-types/impl-serde", 43 | ] 44 | -------------------------------------------------------------------------------- /interpreter/src/error/exit.rs: -------------------------------------------------------------------------------- 1 | use alloc::borrow::Cow; 2 | use core::fmt; 3 | 4 | use crate::opcode::Opcode; 5 | 6 | /// Exit result. 7 | pub type ExitResult = Result; 8 | 9 | /// Exit reason. 10 | #[derive(Clone, Debug, Eq, PartialEq)] 11 | #[cfg_attr( 12 | feature = "scale", 13 | derive(scale_codec::Encode, scale_codec::Decode, scale_info::TypeInfo) 14 | )] 15 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 16 | pub enum ExitError { 17 | /// Machine returns a normal EVM error. 18 | Exception(ExitException), 19 | /// Machine encountered an explicit revert. 20 | Reverted, 21 | /// Machine encountered an error that is not supposed to be normal EVM 22 | /// errors, such as requiring too much memory to execute. 23 | Fatal(ExitFatal), 24 | } 25 | 26 | impl From for ExitResult { 27 | fn from(s: ExitError) -> Self { 28 | Err(s) 29 | } 30 | } 31 | 32 | #[cfg(feature = "std")] 33 | impl std::error::Error for ExitError {} 34 | 35 | impl fmt::Display for ExitError { 36 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 37 | match self { 38 | Self::Exception(_) => f.write_str("EVM exit exception"), 39 | Self::Reverted => f.write_str("EVM internal revert"), 40 | Self::Fatal(_) => f.write_str("EVM fatal error"), 41 | } 42 | } 43 | } 44 | 45 | /// Exit succeed reason. 46 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 47 | #[cfg_attr( 48 | feature = "scale", 49 | derive(scale_codec::Encode, scale_codec::Decode, scale_info::TypeInfo) 50 | )] 51 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 52 | pub enum ExitSucceed { 53 | /// Machine encountered an explicit stop. 54 | Stopped, 55 | /// Machine encountered an explicit return. 56 | Returned, 57 | /// Machine encountered an explicit suicide. 58 | Suicided, 59 | } 60 | 61 | impl From for ExitResult { 62 | fn from(s: ExitSucceed) -> Self { 63 | Ok(s) 64 | } 65 | } 66 | 67 | /// Exit error reason. 68 | #[derive(Clone, Debug, Eq, PartialEq)] 69 | #[cfg_attr( 70 | feature = "scale", 71 | derive(scale_codec::Encode, scale_codec::Decode, scale_info::TypeInfo) 72 | )] 73 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 74 | pub enum ExitException { 75 | /// Trying to pop from an empty stack. 76 | #[cfg_attr(feature = "scale", codec(index = 0))] 77 | StackUnderflow, 78 | /// Trying to push into a stack over stack limit. 79 | #[cfg_attr(feature = "scale", codec(index = 1))] 80 | StackOverflow, 81 | /// Jump destination is invalid. 82 | #[cfg_attr(feature = "scale", codec(index = 2))] 83 | InvalidJump, 84 | /// An opcode accesses memory region, but the region is invalid. 85 | #[cfg_attr(feature = "scale", codec(index = 3))] 86 | InvalidRange, 87 | /// Encountered the designated invalid opcode. 88 | #[cfg_attr(feature = "scale", codec(index = 4))] 89 | DesignatedInvalid, 90 | /// Call stack is too deep (runtime). 91 | #[cfg_attr(feature = "scale", codec(index = 5))] 92 | CallTooDeep, 93 | /// Create opcode encountered collision (runtime). 94 | #[cfg_attr(feature = "scale", codec(index = 6))] 95 | CreateCollision, 96 | /// Create init code exceeds limit (runtime). 97 | #[cfg_attr(feature = "scale", codec(index = 7))] 98 | CreateContractLimit, 99 | 100 | /// Invalid opcode during execution or starting byte is 0xef ([EIP-3541](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3541.md)). 101 | #[cfg_attr(feature = "scale", codec(index = 15))] 102 | InvalidOpcode(Opcode), 103 | 104 | /// An opcode accesses external information, but the request is off offset 105 | /// limit (runtime). 106 | #[cfg_attr(feature = "scale", codec(index = 8))] 107 | OutOfOffset, 108 | /// Execution runs out of gas (runtime). 109 | #[cfg_attr(feature = "scale", codec(index = 9))] 110 | OutOfGas, 111 | /// Not enough fund to start the execution (runtime). 112 | #[cfg_attr(feature = "scale", codec(index = 10))] 113 | OutOfFund, 114 | 115 | /// PC underflowed (unused). 116 | #[allow(clippy::upper_case_acronyms)] 117 | #[cfg_attr(feature = "scale", codec(index = 11))] 118 | PCUnderflow, 119 | 120 | /// Attempt to create an empty account (runtime, unused). 121 | #[cfg_attr(feature = "scale", codec(index = 12))] 122 | CreateEmpty, 123 | 124 | /// Nonce reached maximum value of 2^64-1 125 | /// https://eips.ethereum.org/EIPS/eip-2681 126 | #[cfg_attr(feature = "scale", codec(index = 14))] 127 | MaxNonce, 128 | 129 | /// Other normal errors. 130 | #[cfg_attr(feature = "scale", codec(index = 13))] 131 | Other(Cow<'static, str>), 132 | } 133 | 134 | impl From for ExitResult { 135 | fn from(s: ExitException) -> Self { 136 | Err(ExitError::Exception(s)) 137 | } 138 | } 139 | 140 | impl From for ExitError { 141 | fn from(s: ExitException) -> Self { 142 | Self::Exception(s) 143 | } 144 | } 145 | 146 | /// Exit fatal reason. 147 | #[derive(Clone, Debug, Eq, PartialEq)] 148 | #[cfg_attr( 149 | feature = "scale", 150 | derive(scale_codec::Encode, scale_codec::Decode, scale_info::TypeInfo) 151 | )] 152 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 153 | pub enum ExitFatal { 154 | /// The operation is not supported. 155 | NotSupported, 156 | /// The trap (interrupt) is unhandled. 157 | UnhandledInterrupt, 158 | /// The environment explicitly set call errors as fatal error. 159 | ExceptionAsFatal(ExitException), 160 | /// Already exited. 161 | AlreadyExited, 162 | /// Unfinished execution. 163 | Unfinished, 164 | 165 | /// Other fatal errors. 166 | Other(Cow<'static, str>), 167 | } 168 | 169 | impl From for ExitResult { 170 | fn from(s: ExitFatal) -> Self { 171 | Err(ExitError::Fatal(s)) 172 | } 173 | } 174 | 175 | impl From for ExitError { 176 | fn from(s: ExitFatal) -> Self { 177 | Self::Fatal(s) 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /interpreter/src/error/mod.rs: -------------------------------------------------------------------------------- 1 | mod exit; 2 | mod trap; 3 | 4 | pub use self::{exit::*, trap::*}; 5 | 6 | /// Capture represents the result of execution. 7 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 8 | pub enum Capture { 9 | /// The machine has exited. It cannot be executed again. 10 | Exit(E), 11 | /// The machine has trapped. It is waiting for external information, and can 12 | /// be executed again. 13 | Trap(T), 14 | } 15 | 16 | impl Capture { 17 | pub fn exit(self) -> Option { 18 | match self { 19 | Self::Exit(e) => Some(e), 20 | Self::Trap(_) => None, 21 | } 22 | } 23 | 24 | pub fn trap(self) -> Option { 25 | match self { 26 | Self::Exit(_) => None, 27 | Self::Trap(t) => Some(t), 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /interpreter/src/eval/arithmetic.rs: -------------------------------------------------------------------------------- 1 | use core::ops::Rem; 2 | 3 | use primitive_types::{U256, U512}; 4 | 5 | use crate::utils::I256; 6 | 7 | #[inline] 8 | pub fn div(op1: U256, op2: U256) -> U256 { 9 | if op2 == U256::zero() { 10 | U256::zero() 11 | } else { 12 | op1 / op2 13 | } 14 | } 15 | 16 | #[inline] 17 | pub fn sdiv(op1: U256, op2: U256) -> U256 { 18 | let op1: I256 = op1.into(); 19 | let op2: I256 = op2.into(); 20 | let ret = op1 / op2; 21 | ret.into() 22 | } 23 | 24 | #[inline] 25 | pub fn rem(op1: U256, op2: U256) -> U256 { 26 | if op2 == U256::zero() { 27 | U256::zero() 28 | } else { 29 | op1.rem(op2) 30 | } 31 | } 32 | 33 | #[inline] 34 | pub fn srem(op1: U256, op2: U256) -> U256 { 35 | if op2 == U256::zero() { 36 | U256::zero() 37 | } else { 38 | let op1: I256 = op1.into(); 39 | let op2: I256 = op2.into(); 40 | let ret = op1.rem(op2); 41 | ret.into() 42 | } 43 | } 44 | 45 | #[inline] 46 | pub fn addmod(op1: U256, op2: U256, op3: U256) -> U256 { 47 | let op1: U512 = op1.into(); 48 | let op2: U512 = op2.into(); 49 | let op3: U512 = op3.into(); 50 | 51 | if op3 == U512::zero() { 52 | U256::zero() 53 | } else { 54 | let v = (op1 + op2) % op3; 55 | v.try_into() 56 | .expect("op3 is less than U256::MAX, thus it never overflows; qed") 57 | } 58 | } 59 | 60 | #[inline] 61 | pub fn mulmod(op1: U256, op2: U256, op3: U256) -> U256 { 62 | let op1: U512 = op1.into(); 63 | let op2: U512 = op2.into(); 64 | let op3: U512 = op3.into(); 65 | 66 | if op3 == U512::zero() { 67 | U256::zero() 68 | } else { 69 | let v = (op1 * op2) % op3; 70 | v.try_into() 71 | .expect("op3 is less than U256::MAX, thus it never overflows; qed") 72 | } 73 | } 74 | 75 | #[inline] 76 | pub fn exp(op1: U256, op2: U256) -> U256 { 77 | let mut op1 = op1; 78 | let mut op2 = op2; 79 | let mut r: U256 = 1.into(); 80 | 81 | while op2 != 0.into() { 82 | if op2 & 1.into() != 0.into() { 83 | r = r.overflowing_mul(op1).0; 84 | } 85 | op2 >>= 1; 86 | op1 = op1.overflowing_mul(op1).0; 87 | } 88 | 89 | r 90 | } 91 | 92 | /// In the yellow paper `SIGNEXTEND` is defined to take two inputs, we will call them 93 | /// `x` and `y`, and produce one output. The first `t` bits of the output (numbering from the 94 | /// left, starting from 0) are equal to the `t`-th bit of `y`, where `t` is equal to 95 | /// `256 - 8(x + 1)`. The remaining bits of the output are equal to the corresponding bits of `y`. 96 | /// Note: if `x >= 32` then the output is equal to `y` since `t <= 0`. To efficiently implement 97 | /// this algorithm in the case `x < 32` we do the following. Let `b` be equal to the `t`-th bit 98 | /// of `y` and let `s = 255 - t = 8x + 7` (this is effectively the same index as `t`, but 99 | /// numbering the bits from the right instead of the left). We can create a bit mask which is all 100 | /// zeros up to and including the `t`-th bit, and all ones afterwards by computing the quantity 101 | /// `2^s - 1`. We can use this mask to compute the output depending on the value of `b`. 102 | /// If `b == 1` then the yellow paper says the output should be all ones up to 103 | /// and including the `t`-th bit, followed by the remaining bits of `y`; this is equal to 104 | /// `y | !mask` where `|` is the bitwise `OR` and `!` is bitwise negation. Similarly, if 105 | /// `b == 0` then the yellow paper says the output should start with all zeros, then end with 106 | /// bits from `b`; this is equal to `y & mask` where `&` is bitwise `AND`. 107 | #[inline] 108 | pub fn signextend(op1: U256, op2: U256) -> U256 { 109 | if op1 < U256::from(32) { 110 | // `low_u32` works since op1 < 32 111 | let bit_index = (8 * op1.low_u32() + 7) as usize; 112 | let bit = op2.bit(bit_index); 113 | let mask = (U256::one() << bit_index) - U256::one(); 114 | if bit { 115 | op2 | !mask 116 | } else { 117 | op2 & mask 118 | } 119 | } else { 120 | op2 121 | } 122 | } 123 | 124 | #[cfg(test)] 125 | mod tests { 126 | use super::{signextend, U256}; 127 | 128 | /// Test to ensure new (optimized) `signextend` implementation is equivalent to the previous 129 | /// implementation. 130 | #[test] 131 | fn test_signextend() { 132 | let test_values = vec![ 133 | U256::zero(), 134 | U256::one(), 135 | U256::from(8), 136 | U256::from(10), 137 | U256::from(65), 138 | U256::from(100), 139 | U256::from(128), 140 | U256::from(11) * (U256::one() << 65), 141 | U256::from(7) * (U256::one() << 123), 142 | U256::MAX / 167, 143 | U256::MAX, 144 | ]; 145 | for x in 0..64 { 146 | for y in test_values.iter() { 147 | compare_old_signextend(x.into(), *y); 148 | } 149 | } 150 | } 151 | 152 | fn compare_old_signextend(x: U256, y: U256) { 153 | let old = old_signextend(x, y); 154 | let new = signextend(x, y); 155 | 156 | assert_eq!(old, new); 157 | } 158 | 159 | fn old_signextend(op1: U256, op2: U256) -> U256 { 160 | if op1 > U256::from(32) { 161 | op2 162 | } else { 163 | let mut ret = U256::zero(); 164 | let len: usize = op1.as_usize(); 165 | let t: usize = 8 * (len + 1) - 1; 166 | let t_bit_mask = U256::one() << t; 167 | let t_value = (op2 & t_bit_mask) >> t; 168 | for i in 0..256 { 169 | let bit_mask = U256::one() << i; 170 | let i_value = (op2 & bit_mask) >> i; 171 | if i <= t { 172 | ret = ret.overflowing_add(i_value << i).0; 173 | } else { 174 | ret = ret.overflowing_add(t_value << i).0; 175 | } 176 | } 177 | ret 178 | } 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /interpreter/src/eval/bitwise.rs: -------------------------------------------------------------------------------- 1 | use primitive_types::U256; 2 | 3 | use crate::utils::{Sign, I256}; 4 | 5 | #[inline] 6 | pub fn slt(op1: U256, op2: U256) -> U256 { 7 | let op1: I256 = op1.into(); 8 | let op2: I256 = op2.into(); 9 | 10 | if op1.lt(&op2) { 11 | U256::one() 12 | } else { 13 | U256::zero() 14 | } 15 | } 16 | 17 | #[inline] 18 | pub fn sgt(op1: U256, op2: U256) -> U256 { 19 | let op1: I256 = op1.into(); 20 | let op2: I256 = op2.into(); 21 | 22 | if op1.gt(&op2) { 23 | U256::one() 24 | } else { 25 | U256::zero() 26 | } 27 | } 28 | 29 | #[inline] 30 | pub fn iszero(op1: U256) -> U256 { 31 | if op1 == U256::zero() { 32 | U256::one() 33 | } else { 34 | U256::zero() 35 | } 36 | } 37 | 38 | #[inline] 39 | pub fn not(op1: U256) -> U256 { 40 | !op1 41 | } 42 | 43 | #[inline] 44 | pub fn byte(op1: U256, op2: U256) -> U256 { 45 | let mut ret = U256::zero(); 46 | 47 | for i in 0..256 { 48 | if i < 8 && op1 < 32.into() { 49 | let o: usize = op1.as_usize(); 50 | let t = 255 - (7 - i + 8 * o); 51 | let bit_mask = U256::one() << t; 52 | let value = (op2 & bit_mask) >> t; 53 | ret = ret.overflowing_add(value << i).0; 54 | } 55 | } 56 | 57 | ret 58 | } 59 | 60 | #[inline] 61 | pub fn shl(shift: U256, value: U256) -> U256 { 62 | if value == U256::zero() || shift >= U256::from(256) { 63 | U256::zero() 64 | } else { 65 | let shift: u64 = shift.as_u64(); 66 | value << shift as usize 67 | } 68 | } 69 | 70 | #[inline] 71 | pub fn shr(shift: U256, value: U256) -> U256 { 72 | if value == U256::zero() || shift >= U256::from(256) { 73 | U256::zero() 74 | } else { 75 | let shift: u64 = shift.as_u64(); 76 | value >> shift as usize 77 | } 78 | } 79 | 80 | #[inline] 81 | pub fn sar(shift: U256, value: U256) -> U256 { 82 | let value = I256::from(value); 83 | 84 | if value == I256::zero() || shift >= U256::from(256) { 85 | let I256(sign, _) = value; 86 | match sign { 87 | // value is 0 or >=1, pushing 0 88 | Sign::Plus | Sign::Zero => U256::zero(), 89 | // value is <0, pushing -1 90 | Sign::Minus => I256(Sign::Minus, U256::one()).into(), 91 | } 92 | } else { 93 | let shift: u64 = shift.as_u64(); 94 | 95 | match value.0 { 96 | Sign::Plus | Sign::Zero => value.1 >> shift as usize, 97 | Sign::Minus => { 98 | let shifted = ((value.1.overflowing_sub(U256::one()).0) >> shift as usize) 99 | .overflowing_add(U256::one()) 100 | .0; 101 | I256(Sign::Minus, shifted).into() 102 | } 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /interpreter/src/eval/macros.rs: -------------------------------------------------------------------------------- 1 | macro_rules! try_or_fail { 2 | ($e:expr) => { 3 | match $e { 4 | Ok(v) => v, 5 | Err(e) => return Control::Exit(e.into()), 6 | } 7 | }; 8 | } 9 | 10 | macro_rules! pop_h256 { 11 | ( $machine:expr, $( $x:ident ),* ) => ( 12 | $( 13 | let $x = match $machine.stack.pop() { 14 | Ok(value) => { 15 | let mut hvalue = H256::default(); 16 | value.to_big_endian(&mut hvalue[..]); 17 | hvalue 18 | }, 19 | Err(e) => return Control::Exit(e.into()), 20 | }; 21 | )* 22 | ); 23 | } 24 | 25 | macro_rules! pop_u256 { 26 | ( $machine:expr, $( $x:ident ),* ) => ( 27 | $( 28 | let $x = match $machine.stack.pop() { 29 | Ok(value) => value, 30 | Err(e) => return Control::Exit(e.into()), 31 | }; 32 | )* 33 | ); 34 | } 35 | 36 | macro_rules! push_h256 { 37 | ( $machine:expr, $( $x:expr ),* ) => ( 38 | $( 39 | match $machine.stack.push(U256::from_big_endian(H256::as_ref(&$x))) { 40 | Ok(()) => (), 41 | Err(e) => return Control::Exit(e.into()), 42 | } 43 | )* 44 | ) 45 | } 46 | 47 | macro_rules! push_u256 { 48 | ( $machine:expr, $( $x:expr ),* ) => ( 49 | $( 50 | match $machine.stack.push($x) { 51 | Ok(()) => (), 52 | Err(e) => return Control::Exit(e.into()), 53 | } 54 | )* 55 | ) 56 | } 57 | 58 | macro_rules! op1_u256_fn { 59 | ($machine:expr, $op:path) => {{ 60 | pop_u256!($machine, op1); 61 | let ret = $op(op1); 62 | push_u256!($machine, ret); 63 | 64 | Control::Continue(1) 65 | }}; 66 | } 67 | 68 | macro_rules! op2_u256_bool_ref { 69 | ($machine:expr, $op:ident) => {{ 70 | pop_u256!($machine, op1, op2); 71 | let ret = op1.$op(&op2); 72 | push_u256!($machine, if ret { U256::one() } else { U256::zero() }); 73 | 74 | Control::Continue(1) 75 | }}; 76 | } 77 | 78 | macro_rules! op2_u256 { 79 | ($machine:expr, $op:ident) => {{ 80 | pop_u256!($machine, op1, op2); 81 | let ret = op1.$op(op2); 82 | push_u256!($machine, ret); 83 | 84 | Control::Continue(1) 85 | }}; 86 | } 87 | 88 | macro_rules! op2_u256_tuple { 89 | ($machine:expr, $op:ident) => {{ 90 | pop_u256!($machine, op1, op2); 91 | let (ret, ..) = op1.$op(op2); 92 | push_u256!($machine, ret); 93 | 94 | Control::Continue(1) 95 | }}; 96 | } 97 | 98 | macro_rules! op2_u256_fn { 99 | ($machine:expr, $op:path) => {{ 100 | pop_u256!($machine, op1, op2); 101 | let ret = $op(op1, op2); 102 | push_u256!($machine, ret); 103 | 104 | Control::Continue(1) 105 | }}; 106 | } 107 | 108 | macro_rules! op3_u256_fn { 109 | ($machine:expr, $op:path) => {{ 110 | pop_u256!($machine, op1, op2, op3); 111 | let ret = $op(op1, op2, op3); 112 | push_u256!($machine, ret); 113 | 114 | Control::Continue(1) 115 | }}; 116 | } 117 | 118 | macro_rules! as_usize_or_fail { 119 | ($v:expr) => {{ 120 | if $v > U256::from(usize::MAX) { 121 | return Control::Exit(ExitFatal::NotSupported.into()); 122 | } 123 | 124 | $v.as_usize() 125 | }}; 126 | 127 | ($v:expr, $reason:expr) => {{ 128 | if $v > U256::from(usize::MAX) { 129 | return Control::Exit($reason.into()); 130 | } 131 | 132 | $v.as_usize() 133 | }}; 134 | } 135 | -------------------------------------------------------------------------------- /interpreter/src/eval/misc.rs: -------------------------------------------------------------------------------- 1 | use core::cmp::{max, min}; 2 | 3 | use primitive_types::{H256, U256}; 4 | 5 | use crate::{ 6 | error::{ExitError, ExitException, ExitFatal, ExitSucceed}, 7 | etable::Control, 8 | machine::Machine, 9 | }; 10 | 11 | #[inline] 12 | pub fn codesize(state: &mut Machine) -> Control { 13 | let stack = &mut state.stack; 14 | let code = &state.code; 15 | 16 | match stack.perform_pop0_push1(|| { 17 | let size = U256::from(code.len()); 18 | Ok((size, ())) 19 | }) { 20 | Ok(()) => Control::Continue(1), 21 | Err(e) => Control::Exit(Err(e)), 22 | } 23 | } 24 | 25 | #[inline] 26 | pub fn codecopy(state: &mut Machine) -> Control { 27 | let stack = &mut state.stack; 28 | let memory = &mut state.memory; 29 | let code = &state.code; 30 | 31 | match stack.perform_pop3_push0(|memory_offset, code_offset, len| { 32 | memory.copy_large(*memory_offset, *code_offset, *len, code)?; 33 | Ok(((), ())) 34 | }) { 35 | Ok(()) => Control::Continue(1), 36 | Err(e) => Control::Exit(Err(e)), 37 | } 38 | } 39 | 40 | #[inline] 41 | pub fn calldataload(state: &mut Machine) -> Control { 42 | pop_u256!(state, index); 43 | 44 | let mut load = [0u8; 32]; 45 | #[allow(clippy::needless_range_loop)] 46 | for i in 0..32 { 47 | if let Some(p) = index.checked_add(U256::from(i)) { 48 | if p <= U256::from(usize::MAX) { 49 | let p = p.as_usize(); 50 | if p < state.data.len() { 51 | load[i] = state.data[p]; 52 | } 53 | } 54 | } 55 | } 56 | 57 | push_h256!(state, H256::from(load)); 58 | Control::Continue(1) 59 | } 60 | 61 | #[inline] 62 | pub fn calldatasize(state: &mut Machine) -> Control { 63 | let len = U256::from(state.data.len()); 64 | push_u256!(state, len); 65 | Control::Continue(1) 66 | } 67 | 68 | #[inline] 69 | pub fn calldatacopy(state: &mut Machine) -> Control { 70 | pop_u256!(state, memory_offset, data_offset, len); 71 | 72 | try_or_fail!(state.memory.resize_offset(memory_offset, len)); 73 | if len == U256::zero() { 74 | return Control::Continue(1); 75 | } 76 | 77 | match state 78 | .memory 79 | .copy_large(memory_offset, data_offset, len, &state.data) 80 | { 81 | Ok(()) => Control::Continue(1), 82 | Err(e) => Control::Exit(e.into()), 83 | } 84 | } 85 | 86 | #[inline] 87 | pub fn pop(state: &mut Machine) -> Control { 88 | pop_h256!(state, _val); 89 | Control::Continue(1) 90 | } 91 | 92 | #[inline] 93 | pub fn mload(state: &mut Machine) -> Control { 94 | pop_u256!(state, index); 95 | try_or_fail!(state.memory.resize_offset(index, U256::from(32))); 96 | let index = as_usize_or_fail!(index); 97 | let value = H256::from_slice(&state.memory.get(index, 32)[..]); 98 | push_h256!(state, value); 99 | Control::Continue(1) 100 | } 101 | 102 | /// Support for EIP-5656: MCOPY instruction. 103 | #[inline] 104 | pub fn mcopy(state: &mut Machine) -> Control { 105 | pop_u256!(state, dst, src, len); 106 | try_or_fail!(state.memory.resize_offset(max(dst, src), len)); 107 | 108 | if len.is_zero() { 109 | return Control::Continue(1); 110 | } 111 | 112 | let dst = as_usize_or_fail!(dst); 113 | let src = as_usize_or_fail!(src); 114 | let len = as_usize_or_fail!(len); 115 | state.memory.copy(dst, src, len); 116 | Control::Continue(1) 117 | } 118 | 119 | #[inline] 120 | pub fn mstore(state: &mut Machine) -> Control { 121 | pop_u256!(state, index); 122 | pop_h256!(state, value); 123 | try_or_fail!(state.memory.resize_offset(index, U256::from(32))); 124 | let index = as_usize_or_fail!(index); 125 | match state.memory.set(index, &value[..], Some(32)) { 126 | Ok(()) => Control::Continue(1), 127 | Err(e) => Control::Exit(e.into()), 128 | } 129 | } 130 | 131 | #[inline] 132 | pub fn mstore8(state: &mut Machine) -> Control { 133 | pop_u256!(state, index, value); 134 | try_or_fail!(state.memory.resize_offset(index, U256::one())); 135 | let index = as_usize_or_fail!(index); 136 | let value = (value.low_u32() & 0xff) as u8; 137 | match state.memory.set(index, &[value], Some(1)) { 138 | Ok(()) => Control::Continue(1), 139 | Err(e) => Control::Exit(e.into()), 140 | } 141 | } 142 | 143 | #[inline] 144 | pub fn jump(state: &mut Machine) -> Control { 145 | pop_u256!(state, dest); 146 | let dest = as_usize_or_fail!(dest, ExitException::InvalidJump); 147 | 148 | Control::Jump(dest) 149 | } 150 | 151 | #[inline] 152 | pub fn jumpi(state: &mut Machine) -> Control { 153 | pop_u256!(state, dest); 154 | pop_h256!(state, value); 155 | 156 | if value == H256::zero() { 157 | Control::Continue(1) 158 | } else { 159 | let dest = as_usize_or_fail!(dest, ExitException::InvalidJump); 160 | Control::Jump(dest) 161 | } 162 | } 163 | 164 | #[inline] 165 | pub fn pc(state: &mut Machine, position: usize) -> Control { 166 | push_u256!(state, U256::from(position)); 167 | Control::Continue(1) 168 | } 169 | 170 | #[inline] 171 | pub fn msize(state: &mut Machine) -> Control { 172 | push_u256!(state, state.memory.effective_len()); 173 | Control::Continue(1) 174 | } 175 | 176 | #[inline] 177 | pub fn push(state: &mut Machine, n: usize, position: usize) -> Control { 178 | let end = min(position + 1 + n, state.code.len()); 179 | let slice = &state.code[(position + 1)..end]; 180 | let mut val = [0u8; 32]; 181 | val[(32 - n)..(32 - n + slice.len())].copy_from_slice(slice); 182 | 183 | let result = H256(val); 184 | push_h256!(state, result); 185 | Control::Continue(1 + n) 186 | } 187 | 188 | #[inline] 189 | pub fn dup(state: &mut Machine, n: usize) -> Control { 190 | let value = match state.stack.peek(n - 1) { 191 | Ok(value) => value, 192 | Err(e) => return Control::Exit(e.into()), 193 | }; 194 | push_u256!(state, value); 195 | Control::Continue(1) 196 | } 197 | 198 | #[inline] 199 | pub fn swap(state: &mut Machine, n: usize) -> Control { 200 | let val1 = match state.stack.peek(0) { 201 | Ok(value) => value, 202 | Err(e) => return Control::Exit(e.into()), 203 | }; 204 | let val2 = match state.stack.peek(n) { 205 | Ok(value) => value, 206 | Err(e) => return Control::Exit(e.into()), 207 | }; 208 | match state.stack.set(0, val2) { 209 | Ok(()) => (), 210 | Err(e) => return Control::Exit(e.into()), 211 | } 212 | match state.stack.set(n, val1) { 213 | Ok(()) => (), 214 | Err(e) => return Control::Exit(e.into()), 215 | } 216 | Control::Continue(1) 217 | } 218 | 219 | #[inline] 220 | pub fn ret(state: &mut Machine) -> Control { 221 | pop_u256!(state, start, len); 222 | try_or_fail!(state.memory.resize_offset(start, len)); 223 | state.memory.resize_to_range(start..(start + len)); 224 | state.memory.swap_and_clear(&mut state.retval); 225 | Control::Exit(ExitSucceed::Returned.into()) 226 | } 227 | 228 | #[inline] 229 | pub fn revert(state: &mut Machine) -> Control { 230 | pop_u256!(state, start, len); 231 | try_or_fail!(state.memory.resize_offset(start, len)); 232 | state.memory.resize_to_range(start..(start + len)); 233 | state.memory.swap_and_clear(&mut state.retval); 234 | Control::Exit(ExitError::Reverted.into()) 235 | } 236 | -------------------------------------------------------------------------------- /interpreter/src/interpreter/etable.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | use core::ops::{Deref, DerefMut}; 3 | 4 | use crate::{ 5 | error::{Capture, ExitError, ExitException, ExitFatal, ExitResult, ExitSucceed}, 6 | etable::{Control, EtableSet}, 7 | interpreter::{valids::Valids, Interpreter, RunInterpreter, StepInterpreter}, 8 | machine::{Machine, Stack}, 9 | opcode::Opcode, 10 | }; 11 | 12 | pub struct EtableInterpreter<'etable, ES: EtableSet> { 13 | valids: Valids, 14 | position: usize, 15 | machine: Machine, 16 | etable: &'etable ES, 17 | } 18 | 19 | impl<'etable, ES: EtableSet> Deref for EtableInterpreter<'etable, ES> { 20 | type Target = Machine; 21 | 22 | fn deref(&self) -> &Machine { 23 | &self.machine 24 | } 25 | } 26 | 27 | impl<'etable, ES: EtableSet> DerefMut for EtableInterpreter<'etable, ES> { 28 | fn deref_mut(&mut self) -> &mut Machine { 29 | &mut self.machine 30 | } 31 | } 32 | 33 | impl<'etable, ES> EtableInterpreter<'etable, ES> 34 | where 35 | ES: EtableSet, 36 | { 37 | /// Return a reference of the program counter. 38 | pub const fn position(&self) -> usize { 39 | self.position 40 | } 41 | 42 | pub fn new(machine: Machine, etable: &'etable ES) -> Self { 43 | let valids = Valids::new(&machine.code[..]); 44 | 45 | Self { 46 | machine, 47 | valids, 48 | position: 0, 49 | etable, 50 | } 51 | } 52 | 53 | pub fn deconstruct(self) -> Machine { 54 | self.machine 55 | } 56 | 57 | /// Explicit exit of the machine. Further step will return error. 58 | pub fn exit(&mut self) { 59 | self.position = self.code.len(); 60 | } 61 | 62 | /// Inspect the machine's next opcode and current stack. 63 | pub fn inspect(&self) -> Option<(Opcode, &Stack)> { 64 | self.code 65 | .get(self.position) 66 | .map(|v| (Opcode(*v), &self.stack)) 67 | } 68 | 69 | /// Perform any operation. If the operation fails, then set the machine 70 | /// status to already exited. 71 | pub fn perform Result>( 72 | &mut self, 73 | f: F, 74 | ) -> Result { 75 | match f(self) { 76 | Ok(r) => Ok(r), 77 | Err(e) => { 78 | self.exit(); 79 | Err(e) 80 | } 81 | } 82 | } 83 | 84 | /// Pick the next opcode. 85 | pub fn peek_opcode(&self) -> Option { 86 | self.code.get(self.position).map(|opcode| Opcode(*opcode)) 87 | } 88 | } 89 | 90 | impl<'etable, ES: EtableSet> Interpreter for EtableInterpreter<'etable, ES> { 91 | type State = ES::State; 92 | 93 | fn machine(&self) -> &Machine { 94 | &self.machine 95 | } 96 | 97 | fn machine_mut(&mut self) -> &mut Machine { 98 | &mut self.machine 99 | } 100 | 101 | fn deconstruct(self) -> (ES::State, Vec) { 102 | (self.machine.state, self.machine.retval) 103 | } 104 | 105 | fn advance(&mut self) { 106 | if self.position == self.code.len() { 107 | return; 108 | } 109 | 110 | self.position += 1; 111 | } 112 | } 113 | 114 | impl<'etable, H, Tr, ES> RunInterpreter for EtableInterpreter<'etable, ES> 115 | where 116 | ES: EtableSet, 117 | { 118 | fn run(&mut self, handle: &mut H) -> Capture { 119 | loop { 120 | match self.step(handle) { 121 | Ok(()) => (), 122 | Err(res) => return res, 123 | } 124 | } 125 | } 126 | } 127 | 128 | impl<'etable, H, Tr, ES> StepInterpreter for EtableInterpreter<'etable, ES> 129 | where 130 | ES: EtableSet, 131 | { 132 | #[inline] 133 | fn step(&mut self, handle: &mut H) -> Result<(), Capture> { 134 | if self.is_empty() { 135 | return Err(Capture::Exit(ExitSucceed::Stopped.into())); 136 | } 137 | 138 | let position = self.position; 139 | if position >= self.code.len() { 140 | return Err(Capture::Exit(ExitFatal::AlreadyExited.into())); 141 | } 142 | 143 | let control = self.etable.eval(&mut self.machine, handle, self.position); 144 | 145 | match control { 146 | Control::NoAction => (), 147 | Control::Continue(p) => { 148 | self.position = position + p; 149 | } 150 | Control::Exit(e) => { 151 | self.position = self.code.len(); 152 | return Err(Capture::Exit(e)); 153 | } 154 | Control::Jump(p) => { 155 | if self.valids.is_valid(p) { 156 | self.position = p; 157 | } else { 158 | self.position = self.code.len(); 159 | return Err(Capture::Exit(ExitException::InvalidJump.into())); 160 | } 161 | } 162 | Control::Trap(opcode) => return Err(Capture::Trap(opcode)), 163 | }; 164 | 165 | if self.position >= self.code.len() { 166 | return Err(Capture::Exit(ExitSucceed::Stopped.into())); 167 | } 168 | 169 | Ok(()) 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /interpreter/src/interpreter/mod.rs: -------------------------------------------------------------------------------- 1 | mod etable; 2 | mod valids; 3 | 4 | use alloc::vec::Vec; 5 | 6 | pub use self::etable::EtableInterpreter; 7 | use crate::{ 8 | error::{Capture, ExitResult}, 9 | machine::Machine, 10 | }; 11 | 12 | pub trait Interpreter { 13 | type State; 14 | 15 | fn machine(&self) -> &Machine; 16 | fn machine_mut(&mut self) -> &mut Machine; 17 | 18 | fn deconstruct(self) -> (Self::State, Vec); 19 | fn advance(&mut self); 20 | } 21 | 22 | pub trait RunInterpreter: Interpreter { 23 | fn run(&mut self, handle: &mut H) -> Capture; 24 | } 25 | 26 | pub trait StepInterpreter: Interpreter { 27 | fn step(&mut self, handle: &mut H) -> Result<(), Capture>; 28 | } 29 | -------------------------------------------------------------------------------- /interpreter/src/interpreter/valids.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | 3 | use crate::opcode::Opcode; 4 | 5 | /// Mapping of valid jump destination from code. 6 | #[derive(Clone, Debug, Eq, PartialEq)] 7 | pub struct Valids(Vec); 8 | 9 | impl Valids { 10 | /// Create a new valid mapping from given code bytes. 11 | #[must_use] 12 | pub fn new(code: &[u8]) -> Self { 13 | let mut valids: Vec = Vec::with_capacity(code.len()); 14 | valids.resize(code.len(), false); 15 | 16 | let mut i = 0; 17 | while i < code.len() { 18 | let opcode = Opcode(code[i]); 19 | if opcode == Opcode::JUMPDEST { 20 | valids[i] = true; 21 | i += 1; 22 | } else if let Some(v) = opcode.is_push() { 23 | i += v as usize + 1; 24 | } else { 25 | i += 1; 26 | } 27 | } 28 | 29 | Self(valids) 30 | } 31 | 32 | /// Returns `true` if the position is a valid jump destination. 33 | /// If not, returns `false`. 34 | #[must_use] 35 | pub fn is_valid(&self, position: usize) -> bool { 36 | if position >= self.0.len() { 37 | return false; 38 | } 39 | 40 | self.0[position] 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /interpreter/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Core layer for EVM. 2 | 3 | // #![deny(warnings)] 4 | // #![forbid(unsafe_code, unused_variables, unused_imports)] 5 | #![cfg_attr(not(feature = "std"), no_std)] 6 | 7 | extern crate alloc; 8 | 9 | pub mod error; 10 | pub mod etable; 11 | pub mod eval; 12 | mod interpreter; 13 | pub mod machine; 14 | pub mod opcode; 15 | pub mod runtime; 16 | pub mod utils; 17 | 18 | pub use self::interpreter::{EtableInterpreter, Interpreter, RunInterpreter, StepInterpreter}; 19 | -------------------------------------------------------------------------------- /interpreter/src/machine/memory.rs: -------------------------------------------------------------------------------- 1 | use alloc::{vec, vec::Vec}; 2 | use core::{ 3 | cmp::{max, min}, 4 | mem, 5 | ops::{BitAnd, Not, Range}, 6 | }; 7 | 8 | use primitive_types::U256; 9 | 10 | use crate::error::{ExitException, ExitFatal}; 11 | 12 | /// A sequencial memory. It uses Rust's `Vec` for internal 13 | /// representation. 14 | #[derive(Clone, Debug)] 15 | pub struct Memory { 16 | data: Vec, 17 | effective_len: U256, 18 | limit: usize, 19 | } 20 | 21 | impl Memory { 22 | /// Create a new memory with the given limit. 23 | #[must_use] 24 | pub fn new(limit: usize) -> Self { 25 | Self { 26 | data: Vec::new(), 27 | effective_len: U256::zero(), 28 | limit, 29 | } 30 | } 31 | 32 | /// Memory limit. 33 | #[must_use] 34 | pub const fn limit(&self) -> usize { 35 | self.limit 36 | } 37 | 38 | /// Get the length of the current memory range. 39 | #[must_use] 40 | pub fn len(&self) -> usize { 41 | self.data.len() 42 | } 43 | 44 | /// Get the effective length. 45 | #[must_use] 46 | pub const fn effective_len(&self) -> U256 { 47 | self.effective_len 48 | } 49 | 50 | /// Return true if current effective memory range is zero. 51 | #[must_use] 52 | pub fn is_empty(&self) -> bool { 53 | self.len() == 0 54 | } 55 | 56 | /// Return the full memory. 57 | #[must_use] 58 | pub const fn data(&self) -> &Vec { 59 | &self.data 60 | } 61 | 62 | pub(crate) fn swap_and_clear(&mut self, other: &mut Vec) { 63 | mem::swap(&mut self.data, other); 64 | self.data = Vec::new(); 65 | self.effective_len = U256::zero(); 66 | } 67 | 68 | /// Resize the memory, making it cover the memory region of `offset..(offset + len)`, 69 | /// with 32 bytes as the step. If the length is zero, this function 70 | /// does nothing. 71 | pub fn resize_offset(&mut self, offset: U256, len: U256) -> Result<(), ExitException> { 72 | if len == U256::zero() { 73 | return Ok(()); 74 | } 75 | 76 | offset 77 | .checked_add(len) 78 | .map_or(Err(ExitException::InvalidRange), |end| self.resize_end(end)) 79 | } 80 | 81 | /// Resize the memory, making it cover to `end`, with 32 bytes as the step. 82 | pub fn resize_end(&mut self, end: U256) -> Result<(), ExitException> { 83 | if end > self.effective_len { 84 | let new_end = next_multiple_of_32(end).ok_or(ExitException::InvalidRange)?; 85 | self.effective_len = new_end; 86 | } 87 | 88 | Ok(()) 89 | } 90 | 91 | /// Resize to range. Used for return value. 92 | pub fn resize_to_range(&mut self, return_range: Range) { 93 | let ret = if return_range.start > U256::from(usize::MAX) { 94 | vec![0; (return_range.end - return_range.start).as_usize()] 95 | } else if return_range.end > U256::from(usize::MAX) { 96 | let mut ret = self.get( 97 | return_range.start.as_usize(), 98 | usize::MAX - return_range.start.as_usize(), 99 | ); 100 | while ret.len() < (return_range.end - return_range.start).as_usize() { 101 | ret.push(0); 102 | } 103 | ret 104 | } else { 105 | self.get( 106 | return_range.start.as_usize(), 107 | (return_range.end - return_range.start).as_usize(), 108 | ) 109 | }; 110 | self.data = ret; 111 | self.effective_len = return_range.end - return_range.start; 112 | } 113 | 114 | /// Get memory region at given offset. 115 | /// 116 | /// ## Panics 117 | /// 118 | /// Value of `size` is considered trusted. If they're too large, 119 | /// the program can run out of memory, or it can overflow. 120 | #[must_use] 121 | pub fn get(&self, offset: usize, size: usize) -> Vec { 122 | let mut ret = vec![0; size]; 123 | 124 | #[allow(clippy::needless_range_loop)] 125 | for index in 0..size { 126 | let position = if let Some(position) = offset.checked_add(index) { 127 | position 128 | } else { 129 | break; 130 | }; 131 | 132 | if position >= self.data.len() { 133 | break; 134 | } 135 | 136 | ret[index] = self.data[position]; 137 | } 138 | 139 | ret 140 | } 141 | 142 | /// Set memory region at given offset. The offset and value is considered 143 | /// untrusted. 144 | pub fn set( 145 | &mut self, 146 | offset: usize, 147 | value: &[u8], 148 | target_size: Option, 149 | ) -> Result<(), ExitFatal> { 150 | let target_size = target_size.unwrap_or(value.len()); 151 | if target_size == 0 { 152 | return Ok(()); 153 | } 154 | 155 | if offset 156 | .checked_add(target_size) 157 | .map(|pos| pos > self.limit) 158 | .unwrap_or(true) 159 | { 160 | return Err(ExitFatal::NotSupported); 161 | } 162 | 163 | if self.data.len() < offset + target_size { 164 | self.data.resize(offset + target_size, 0); 165 | } 166 | 167 | if target_size > value.len() { 168 | self.data[offset..((value.len()) + offset)].clone_from_slice(value); 169 | for index in (value.len())..target_size { 170 | self.data[offset + index] = 0; 171 | } 172 | } else { 173 | self.data[offset..(target_size + offset)].clone_from_slice(&value[..target_size]); 174 | } 175 | 176 | Ok(()) 177 | } 178 | 179 | /// Copy `data` into the memory, of given `len`. 180 | pub fn copy_large( 181 | &mut self, 182 | memory_offset: U256, 183 | data_offset: U256, 184 | len: U256, 185 | data: &[u8], 186 | ) -> Result<(), ExitFatal> { 187 | // Needed to pass ethereum test defined in 188 | // https://github.com/ethereum/tests/commit/17f7e7a6c64bb878c1b6af9dc8371b46c133e46d 189 | // (regardless of other inputs, a zero-length copy is defined to be a no-op). 190 | // TODO: refactor `set` and `copy_large` (see 191 | // https://github.com/rust-blockchain/evm/pull/40#discussion_r677180794) 192 | if len.is_zero() { 193 | return Ok(()); 194 | } 195 | 196 | let memory_offset = if memory_offset > U256::from(usize::MAX) { 197 | return Err(ExitFatal::NotSupported); 198 | } else { 199 | memory_offset.as_usize() 200 | }; 201 | 202 | let ulen = if len > U256::from(usize::MAX) { 203 | return Err(ExitFatal::NotSupported); 204 | } else { 205 | len.as_usize() 206 | }; 207 | 208 | let data: &[u8] = data_offset.checked_add(len).map_or(&[], |end| { 209 | if end > U256::from(usize::MAX) { 210 | &[] 211 | } else { 212 | let data_offset = data_offset.as_usize(); 213 | let end = end.as_usize(); 214 | 215 | if data_offset > data.len() { 216 | &[] 217 | } else { 218 | &data[data_offset..min(end, data.len())] 219 | } 220 | } 221 | }); 222 | 223 | self.set(memory_offset, data, Some(ulen)) 224 | } 225 | 226 | /// Copies part of the memory inside another part of itself. 227 | pub fn copy(&mut self, dst: usize, src: usize, len: usize) { 228 | let resize_offset = max(dst, src); 229 | if self.data.len() < resize_offset + len { 230 | self.data.resize(resize_offset + len, 0); 231 | } 232 | self.data.copy_within(src..src + len, dst); 233 | } 234 | } 235 | 236 | /// Rounds up `x` to the closest multiple of 32. If `x % 32 == 0` then `x` is returned. 237 | #[inline] 238 | fn next_multiple_of_32(x: U256) -> Option { 239 | let r = x.low_u32().bitand(31).not().wrapping_add(1).bitand(31); 240 | x.checked_add(r.into()) 241 | } 242 | 243 | #[cfg(test)] 244 | mod tests { 245 | use super::{next_multiple_of_32, Memory, U256}; 246 | 247 | #[test] 248 | fn test_next_multiple_of_32() { 249 | // next_multiple_of_32 returns x when it is a multiple of 32 250 | for i in 0..32 { 251 | let x = U256::from(i * 32); 252 | assert_eq!(Some(x), next_multiple_of_32(x)); 253 | } 254 | 255 | // next_multiple_of_32 rounds up to the nearest multiple of 32 when `x % 32 != 0` 256 | for x in 0..1024 { 257 | if x % 32 == 0 { 258 | continue; 259 | } 260 | let next_multiple = x + 32 - (x % 32); 261 | assert_eq!( 262 | Some(U256::from(next_multiple)), 263 | next_multiple_of_32(x.into()) 264 | ); 265 | } 266 | 267 | // next_multiple_of_32 returns None when the next multiple of 32 is too big 268 | let last_multiple_of_32 = U256::MAX & !U256::from(31); 269 | for i in 0..63 { 270 | let x = U256::MAX - U256::from(i); 271 | if x > last_multiple_of_32 { 272 | assert_eq!(None, next_multiple_of_32(x)); 273 | } else { 274 | assert_eq!(Some(last_multiple_of_32), next_multiple_of_32(x)); 275 | } 276 | } 277 | } 278 | 279 | #[test] 280 | fn test_memory_copy_works() { 281 | // Create a new instance of memory 282 | let mut memory = Memory::new(100usize); 283 | 284 | // Set the [0,0,0,1,2,3,4] array as memory data. 285 | // 286 | // We insert the [1,2,3,4] array on index 3, 287 | // that's why we have the zero padding at the beginning. 288 | memory.set(3usize, &[1u8, 2u8, 3u8, 4u8], None).unwrap(); 289 | assert_eq!(memory.data(), &[0u8, 0u8, 0u8, 1u8, 2u8, 3u8, 4u8].to_vec()); 290 | 291 | // Copy 1 byte into index 0. 292 | // As the length is 1, we only copy the byte present on index 3. 293 | memory.copy(0usize, 3usize, 1usize); 294 | 295 | // Now the new memory data results in [1,0,0,1,2,3,4] 296 | assert_eq!(memory.data(), &[1u8, 0u8, 0u8, 1u8, 2u8, 3u8, 4u8].to_vec()); 297 | } 298 | 299 | #[test] 300 | fn test_memory_copy_resize() { 301 | // Create a new instance of memory 302 | let mut memory = Memory::new(100usize); 303 | 304 | // Set the [0,0,0,1,2,3,4] array as memory data. 305 | // 306 | // We insert the [1,2,3,4] array on index 3, 307 | // that's why we have the zero padding at the beginning. 308 | memory.set(3usize, &[1u8, 2u8, 3u8, 4u8], None).unwrap(); 309 | assert_eq!(memory.data(), &[0u8, 0u8, 0u8, 1u8, 2u8, 3u8, 4u8].to_vec()); 310 | 311 | // Copy 2 bytes into index 3. 312 | // As the length is 2, we copy the bytes present on indexes 6 and 7, 313 | // which are [4,0]. 314 | memory.copy(3usize, 6usize, 2usize); 315 | 316 | // Now the new memory data results in [0, 0, 0, 4, 0, 3, 4, 0]. 317 | // An extra element is added due to resizing. 318 | assert_eq!( 319 | memory.data(), 320 | &[0u8, 0u8, 0u8, 4u8, 0u8, 3u8, 4u8, 0u8].to_vec() 321 | ); 322 | } 323 | } 324 | -------------------------------------------------------------------------------- /interpreter/src/machine/mod.rs: -------------------------------------------------------------------------------- 1 | mod memory; 2 | mod stack; 3 | 4 | use alloc::{rc::Rc, vec::Vec}; 5 | 6 | pub use self::{memory::Memory, stack::Stack}; 7 | 8 | /// Core execution layer for EVM. 9 | pub struct Machine { 10 | /// Program data. 11 | pub(crate) data: Rc>, 12 | /// Program code. 13 | pub(crate) code: Rc>, 14 | /// Return value. Note the difference between `retbuf`. 15 | /// A `retval` holds what's returned by the current machine, with `RETURN` or `REVERT` opcode. 16 | /// A `retbuf` holds the buffer of returned value by sub-calls. 17 | pub retval: Vec, 18 | /// Memory. 19 | pub memory: Memory, 20 | /// Stack. 21 | pub stack: Stack, 22 | /// Extra state, 23 | pub state: S, 24 | } 25 | 26 | impl Machine { 27 | /// Create a new machine with given code and data. 28 | pub fn new( 29 | code: Rc>, 30 | data: Rc>, 31 | stack_limit: usize, 32 | memory_limit: usize, 33 | state: S, 34 | ) -> Self { 35 | Self { 36 | data, 37 | code, 38 | retval: Vec::new(), 39 | memory: Memory::new(memory_limit), 40 | stack: Stack::new(stack_limit), 41 | state, 42 | } 43 | } 44 | 45 | /// Machine code. 46 | pub fn code(&self) -> &[u8] { 47 | &self.code 48 | } 49 | 50 | /// Whether the machine has empty code. 51 | #[must_use] 52 | pub fn is_empty(&self) -> bool { 53 | self.code.is_empty() 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /interpreter/src/machine/stack.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | 3 | use primitive_types::U256; 4 | 5 | use crate::error::{ExitError, ExitException}; 6 | 7 | /// EVM stack. 8 | #[derive(Clone, Debug)] 9 | pub struct Stack { 10 | data: Vec, 11 | limit: usize, 12 | } 13 | 14 | macro_rules! impl_perform_popn_pushn { 15 | ( 16 | $name:ident, 17 | $pop_len:expr, 18 | $push_len:expr, 19 | ($($peek_pop:expr),*), 20 | ($($peek_push:expr),*), 21 | $pop_pushn_f:ident 22 | ) => { 23 | /// Pop $pop_len values from the stack, and then push $push_len values 24 | /// into the stack. 25 | /// 26 | /// If `f` returns error, then the stack will not be changed. 27 | #[allow(unused_parens)] 28 | pub fn $name(&mut self, f: F) -> Result where 29 | F: FnOnce( 30 | $(impl_perform_popn_pushn!(INTERNAL_TYPE_RU256, $peek_pop)),* 31 | ) -> Result<(($(impl_perform_popn_pushn!(INTERNAL_TYPE_U256, $peek_push)),*), R), ExitError> 32 | { 33 | match self.check_pop_push($pop_len, $push_len) { 34 | Ok(()) => (), 35 | Err(e) => return Err(e.into()), 36 | } 37 | 38 | let (p, ret) = match f($(self.unchecked_peek($peek_pop)),*) { 39 | Ok(p1) => p1, 40 | Err(e) => return Err(e.into()), 41 | }; 42 | self.$pop_pushn_f($pop_len, p); 43 | 44 | Ok(ret) 45 | } 46 | }; 47 | (INTERNAL_TYPE_RU256, $e:expr) => { &U256 }; 48 | (INTERNAL_TYPE_U256, $e:expr) => { U256 }; 49 | } 50 | 51 | impl Stack { 52 | /// Create a new stack with given limit. 53 | #[must_use] 54 | pub const fn new(limit: usize) -> Self { 55 | Self { 56 | data: Vec::new(), 57 | limit, 58 | } 59 | } 60 | 61 | /// Stack limit. 62 | #[inline] 63 | #[must_use] 64 | pub const fn limit(&self) -> usize { 65 | self.limit 66 | } 67 | 68 | /// Stack length. 69 | #[inline] 70 | #[must_use] 71 | pub fn len(&self) -> usize { 72 | self.data.len() 73 | } 74 | 75 | /// Whether the stack is empty. 76 | #[inline] 77 | #[must_use] 78 | pub fn is_empty(&self) -> bool { 79 | self.data.is_empty() 80 | } 81 | 82 | /// Stack data. 83 | #[inline] 84 | #[must_use] 85 | pub const fn data(&self) -> &Vec { 86 | &self.data 87 | } 88 | 89 | /// Clear the stack. 90 | pub fn clear(&mut self) { 91 | self.data.clear(); 92 | } 93 | 94 | /// Pop a value from the stack. 95 | /// If the stack is already empty, returns the `StackUnderflow` error. 96 | #[inline] 97 | pub fn pop(&mut self) -> Result { 98 | self.data.pop().ok_or(ExitException::StackUnderflow) 99 | } 100 | 101 | /// Push a new value into the stack. 102 | /// If it exceeds the stack limit, returns `StackOverflow` error and 103 | /// leaves the stack unchanged. 104 | #[inline] 105 | pub fn push(&mut self, value: U256) -> Result<(), ExitException> { 106 | if self.data.len() + 1 > self.limit { 107 | return Err(ExitException::StackOverflow); 108 | } 109 | self.data.push(value); 110 | Ok(()) 111 | } 112 | 113 | /// Check whether it's possible to pop and push enough items in the stack. 114 | pub fn check_pop_push(&self, pop: usize, push: usize) -> Result<(), ExitException> { 115 | if self.data.len() < pop { 116 | return Err(ExitException::StackUnderflow); 117 | } 118 | if self.data.len() - pop + push + 1 > self.limit { 119 | return Err(ExitException::StackOverflow); 120 | } 121 | Ok(()) 122 | } 123 | 124 | fn unchecked_peek(&self, no_from_top: usize) -> &U256 { 125 | &self.data[self.data.len() - no_from_top - 1] 126 | } 127 | 128 | fn unchecked_pop_push1(&mut self, pop: usize, p1: U256) { 129 | for _ in 0..pop { 130 | self.data.pop(); 131 | } 132 | self.data.push(p1); 133 | } 134 | 135 | fn unchecked_pop_push0(&mut self, pop: usize, _p1: ()) { 136 | for _ in 0..pop { 137 | self.data.pop(); 138 | } 139 | } 140 | 141 | /// Peek a value at given index for the stack, where the top of 142 | /// the stack is at index `0`. If the index is too large, 143 | /// `StackError::Underflow` is returned. 144 | #[inline] 145 | pub fn peek(&self, no_from_top: usize) -> Result { 146 | if self.data.len() > no_from_top { 147 | Ok(self.data[self.data.len() - no_from_top - 1]) 148 | } else { 149 | Err(ExitException::StackUnderflow) 150 | } 151 | } 152 | 153 | /// Set a value at given index for the stack, where the top of the 154 | /// stack is at index `0`. If the index is too large, 155 | /// `StackError::Underflow` is returned. 156 | #[inline] 157 | pub fn set(&mut self, no_from_top: usize, val: U256) -> Result<(), ExitException> { 158 | if self.data.len() > no_from_top { 159 | let len = self.data.len(); 160 | self.data[len - no_from_top - 1] = val; 161 | Ok(()) 162 | } else { 163 | Err(ExitException::StackUnderflow) 164 | } 165 | } 166 | 167 | impl_perform_popn_pushn!(perform_pop0_push1, 0, 1, (), (0), unchecked_pop_push1); 168 | impl_perform_popn_pushn!(perform_pop1_push0, 1, 0, (0), (), unchecked_pop_push0); 169 | impl_perform_popn_pushn!(perform_pop1_push1, 1, 1, (0), (0), unchecked_pop_push1); 170 | impl_perform_popn_pushn!(perform_pop2_push1, 2, 1, (0, 1), (0), unchecked_pop_push1); 171 | impl_perform_popn_pushn!(perform_pop3_push0, 3, 0, (0, 1, 2), (), unchecked_pop_push0); 172 | impl_perform_popn_pushn!( 173 | perform_pop4_push0, 174 | 4, 175 | 0, 176 | (0, 1, 2, 3), 177 | (), 178 | unchecked_pop_push0 179 | ); 180 | impl_perform_popn_pushn!( 181 | perform_pop6_push0, 182 | 6, 183 | 0, 184 | (0, 1, 2, 3, 4, 5), 185 | (), 186 | unchecked_pop_push0 187 | ); 188 | impl_perform_popn_pushn!( 189 | perform_pop7_push0, 190 | 7, 191 | 0, 192 | (0, 1, 2, 3, 4, 5, 6), 193 | (), 194 | unchecked_pop_push0 195 | ); 196 | } 197 | -------------------------------------------------------------------------------- /interpreter/src/opcode.rs: -------------------------------------------------------------------------------- 1 | /// Opcode enum. One-to-one corresponding to an `u8` value. 2 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 3 | #[cfg_attr( 4 | feature = "scale", 5 | derive(scale_codec::Encode, scale_codec::Decode, scale_info::TypeInfo) 6 | )] 7 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 8 | pub struct Opcode(pub u8); 9 | 10 | // Core opcodes. 11 | impl Opcode { 12 | /// `STOP` 13 | pub const STOP: Opcode = Opcode(0x00); 14 | /// `ADD` 15 | pub const ADD: Opcode = Opcode(0x01); 16 | /// `MUL` 17 | pub const MUL: Opcode = Opcode(0x02); 18 | /// `SUB` 19 | pub const SUB: Opcode = Opcode(0x03); 20 | /// `DIV` 21 | pub const DIV: Opcode = Opcode(0x04); 22 | /// `SDIV` 23 | pub const SDIV: Opcode = Opcode(0x05); 24 | /// `MOD` 25 | pub const MOD: Opcode = Opcode(0x06); 26 | /// `SMOD` 27 | pub const SMOD: Opcode = Opcode(0x07); 28 | /// `ADDMOD` 29 | pub const ADDMOD: Opcode = Opcode(0x08); 30 | /// `MULMOD` 31 | pub const MULMOD: Opcode = Opcode(0x09); 32 | /// `EXP` 33 | pub const EXP: Opcode = Opcode(0x0a); 34 | /// `SIGNEXTEND` 35 | pub const SIGNEXTEND: Opcode = Opcode(0x0b); 36 | 37 | /// `LT` 38 | pub const LT: Opcode = Opcode(0x10); 39 | /// `GT` 40 | pub const GT: Opcode = Opcode(0x11); 41 | /// `SLT` 42 | pub const SLT: Opcode = Opcode(0x12); 43 | /// `SGT` 44 | pub const SGT: Opcode = Opcode(0x13); 45 | /// `EQ` 46 | pub const EQ: Opcode = Opcode(0x14); 47 | /// `ISZERO` 48 | pub const ISZERO: Opcode = Opcode(0x15); 49 | /// `AND` 50 | pub const AND: Opcode = Opcode(0x16); 51 | /// `OR` 52 | pub const OR: Opcode = Opcode(0x17); 53 | /// `XOR` 54 | pub const XOR: Opcode = Opcode(0x18); 55 | /// `NOT` 56 | pub const NOT: Opcode = Opcode(0x19); 57 | /// `BYTE` 58 | pub const BYTE: Opcode = Opcode(0x1a); 59 | 60 | /// `SHL` 61 | pub const SHL: Opcode = Opcode(0x1b); 62 | /// `SHR` 63 | pub const SHR: Opcode = Opcode(0x1c); 64 | /// `SAR` 65 | pub const SAR: Opcode = Opcode(0x1d); 66 | 67 | /// `CALLDATALOAD` 68 | pub const CALLDATALOAD: Opcode = Opcode(0x35); 69 | /// `CALLDATASIZE` 70 | pub const CALLDATASIZE: Opcode = Opcode(0x36); 71 | /// `CALLDATACOPY` 72 | pub const CALLDATACOPY: Opcode = Opcode(0x37); 73 | /// `CODESIZE` 74 | pub const CODESIZE: Opcode = Opcode(0x38); 75 | /// `CODECOPY` 76 | pub const CODECOPY: Opcode = Opcode(0x39); 77 | 78 | /// `POP` 79 | pub const POP: Opcode = Opcode(0x50); 80 | /// `MLOAD` 81 | pub const MLOAD: Opcode = Opcode(0x51); 82 | /// `MSTORE` 83 | pub const MSTORE: Opcode = Opcode(0x52); 84 | /// `MSTORE8` 85 | pub const MSTORE8: Opcode = Opcode(0x53); 86 | 87 | /// `JUMP` 88 | pub const JUMP: Opcode = Opcode(0x56); 89 | /// `JUMPI` 90 | pub const JUMPI: Opcode = Opcode(0x57); 91 | /// `PC` 92 | pub const PC: Opcode = Opcode(0x58); 93 | /// `MSIZE` 94 | pub const MSIZE: Opcode = Opcode(0x59); 95 | 96 | /// `JUMPDEST` 97 | pub const JUMPDEST: Opcode = Opcode(0x5b); 98 | /// `MCOPY` 99 | pub const MCOPY: Opcode = Opcode(0x5e); 100 | 101 | /// `PUSHn` 102 | pub const PUSH0: Opcode = Opcode(0x5f); 103 | pub const PUSH1: Opcode = Opcode(0x60); 104 | pub const PUSH2: Opcode = Opcode(0x61); 105 | pub const PUSH3: Opcode = Opcode(0x62); 106 | pub const PUSH4: Opcode = Opcode(0x63); 107 | pub const PUSH5: Opcode = Opcode(0x64); 108 | pub const PUSH6: Opcode = Opcode(0x65); 109 | pub const PUSH7: Opcode = Opcode(0x66); 110 | pub const PUSH8: Opcode = Opcode(0x67); 111 | pub const PUSH9: Opcode = Opcode(0x68); 112 | pub const PUSH10: Opcode = Opcode(0x69); 113 | pub const PUSH11: Opcode = Opcode(0x6a); 114 | pub const PUSH12: Opcode = Opcode(0x6b); 115 | pub const PUSH13: Opcode = Opcode(0x6c); 116 | pub const PUSH14: Opcode = Opcode(0x6d); 117 | pub const PUSH15: Opcode = Opcode(0x6e); 118 | pub const PUSH16: Opcode = Opcode(0x6f); 119 | pub const PUSH17: Opcode = Opcode(0x70); 120 | pub const PUSH18: Opcode = Opcode(0x71); 121 | pub const PUSH19: Opcode = Opcode(0x72); 122 | pub const PUSH20: Opcode = Opcode(0x73); 123 | pub const PUSH21: Opcode = Opcode(0x74); 124 | pub const PUSH22: Opcode = Opcode(0x75); 125 | pub const PUSH23: Opcode = Opcode(0x76); 126 | pub const PUSH24: Opcode = Opcode(0x77); 127 | pub const PUSH25: Opcode = Opcode(0x78); 128 | pub const PUSH26: Opcode = Opcode(0x79); 129 | pub const PUSH27: Opcode = Opcode(0x7a); 130 | pub const PUSH28: Opcode = Opcode(0x7b); 131 | pub const PUSH29: Opcode = Opcode(0x7c); 132 | pub const PUSH30: Opcode = Opcode(0x7d); 133 | pub const PUSH31: Opcode = Opcode(0x7e); 134 | pub const PUSH32: Opcode = Opcode(0x7f); 135 | 136 | /// `DUPn` 137 | pub const DUP1: Opcode = Opcode(0x80); 138 | pub const DUP2: Opcode = Opcode(0x81); 139 | pub const DUP3: Opcode = Opcode(0x82); 140 | pub const DUP4: Opcode = Opcode(0x83); 141 | pub const DUP5: Opcode = Opcode(0x84); 142 | pub const DUP6: Opcode = Opcode(0x85); 143 | pub const DUP7: Opcode = Opcode(0x86); 144 | pub const DUP8: Opcode = Opcode(0x87); 145 | pub const DUP9: Opcode = Opcode(0x88); 146 | pub const DUP10: Opcode = Opcode(0x89); 147 | pub const DUP11: Opcode = Opcode(0x8a); 148 | pub const DUP12: Opcode = Opcode(0x8b); 149 | pub const DUP13: Opcode = Opcode(0x8c); 150 | pub const DUP14: Opcode = Opcode(0x8d); 151 | pub const DUP15: Opcode = Opcode(0x8e); 152 | pub const DUP16: Opcode = Opcode(0x8f); 153 | 154 | /// `SWAPn` 155 | pub const SWAP1: Opcode = Opcode(0x90); 156 | pub const SWAP2: Opcode = Opcode(0x91); 157 | pub const SWAP3: Opcode = Opcode(0x92); 158 | pub const SWAP4: Opcode = Opcode(0x93); 159 | pub const SWAP5: Opcode = Opcode(0x94); 160 | pub const SWAP6: Opcode = Opcode(0x95); 161 | pub const SWAP7: Opcode = Opcode(0x96); 162 | pub const SWAP8: Opcode = Opcode(0x97); 163 | pub const SWAP9: Opcode = Opcode(0x98); 164 | pub const SWAP10: Opcode = Opcode(0x99); 165 | pub const SWAP11: Opcode = Opcode(0x9a); 166 | pub const SWAP12: Opcode = Opcode(0x9b); 167 | pub const SWAP13: Opcode = Opcode(0x9c); 168 | pub const SWAP14: Opcode = Opcode(0x9d); 169 | pub const SWAP15: Opcode = Opcode(0x9e); 170 | pub const SWAP16: Opcode = Opcode(0x9f); 171 | 172 | /// See [EIP-3541](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3541.md) 173 | pub const EOFMAGIC: Opcode = Opcode(0xef); 174 | 175 | /// `RETURN` 176 | pub const RETURN: Opcode = Opcode(0xf3); 177 | 178 | /// `REVERT` 179 | pub const REVERT: Opcode = Opcode(0xfd); 180 | 181 | /// `INVALID` 182 | pub const INVALID: Opcode = Opcode(0xfe); 183 | } 184 | 185 | // External opcodes 186 | impl Opcode { 187 | /// `SHA3` 188 | pub const SHA3: Opcode = Opcode(0x20); 189 | 190 | /// `ADDRESS` 191 | pub const ADDRESS: Opcode = Opcode(0x30); 192 | /// `BALANCE` 193 | pub const BALANCE: Opcode = Opcode(0x31); 194 | /// `ORIGIN` 195 | pub const ORIGIN: Opcode = Opcode(0x32); 196 | /// `CALLER` 197 | pub const CALLER: Opcode = Opcode(0x33); 198 | /// `CALLVALUE` 199 | pub const CALLVALUE: Opcode = Opcode(0x34); 200 | 201 | /// `GASPRICE` 202 | pub const GASPRICE: Opcode = Opcode(0x3a); 203 | /// `EXTCODESIZE` 204 | pub const EXTCODESIZE: Opcode = Opcode(0x3b); 205 | /// `EXTCODECOPY` 206 | pub const EXTCODECOPY: Opcode = Opcode(0x3c); 207 | /// `RETURNDATASIZE` 208 | pub const RETURNDATASIZE: Opcode = Opcode(0x3d); 209 | /// `RETURNDATACOPY` 210 | pub const RETURNDATACOPY: Opcode = Opcode(0x3e); 211 | /// `EXTCODEHASH` 212 | pub const EXTCODEHASH: Opcode = Opcode(0x3f); 213 | 214 | /// `BLOCKHASH` 215 | pub const BLOCKHASH: Opcode = Opcode(0x40); 216 | /// `COINBASE` 217 | pub const COINBASE: Opcode = Opcode(0x41); 218 | /// `TIMESTAMP` 219 | pub const TIMESTAMP: Opcode = Opcode(0x42); 220 | /// `NUMBER` 221 | pub const NUMBER: Opcode = Opcode(0x43); 222 | /// `DIFFICULTY` 223 | pub const DIFFICULTY: Opcode = Opcode(0x44); 224 | /// `GASLIMIT` 225 | pub const GASLIMIT: Opcode = Opcode(0x45); 226 | /// `CHAINID` 227 | pub const CHAINID: Opcode = Opcode(0x46); 228 | /// `SELFBALANCE` 229 | pub const SELFBALANCE: Opcode = Opcode(0x47); 230 | /// `BASEFEE` 231 | pub const BASEFEE: Opcode = Opcode(0x48); 232 | 233 | /// `SLOAD` 234 | pub const SLOAD: Opcode = Opcode(0x54); 235 | /// `SSTORE` 236 | pub const SSTORE: Opcode = Opcode(0x55); 237 | 238 | /// `GAS` 239 | pub const GAS: Opcode = Opcode(0x5a); 240 | 241 | /// `TLOAD` 242 | pub const TLOAD: Opcode = Opcode(0x5c); 243 | /// `TSTORE` 244 | pub const TSTORE: Opcode = Opcode(0x5d); 245 | 246 | /// `LOGn` 247 | pub const LOG0: Opcode = Opcode(0xa0); 248 | pub const LOG1: Opcode = Opcode(0xa1); 249 | pub const LOG2: Opcode = Opcode(0xa2); 250 | pub const LOG3: Opcode = Opcode(0xa3); 251 | pub const LOG4: Opcode = Opcode(0xa4); 252 | 253 | /// `CREATE` 254 | pub const CREATE: Opcode = Opcode(0xf0); 255 | /// `CALL` 256 | pub const CALL: Opcode = Opcode(0xf1); 257 | /// `CALLCODE` 258 | pub const CALLCODE: Opcode = Opcode(0xf2); 259 | 260 | /// `DELEGATECALL` 261 | pub const DELEGATECALL: Opcode = Opcode(0xf4); 262 | /// `CREATE2` 263 | pub const CREATE2: Opcode = Opcode(0xf5); 264 | 265 | /// `STATICCALL` 266 | pub const STATICCALL: Opcode = Opcode(0xfa); 267 | 268 | /// `SUICIDE` 269 | pub const SUICIDE: Opcode = Opcode(0xff); 270 | } 271 | 272 | impl Opcode { 273 | /// Whether the opcode is a push opcode. 274 | #[must_use] 275 | pub fn is_push(&self) -> Option { 276 | let value = self.0; 277 | if (0x60..=0x7f).contains(&value) { 278 | Some(value - 0x60 + 1) 279 | } else { 280 | None 281 | } 282 | } 283 | 284 | #[inline] 285 | #[must_use] 286 | pub const fn as_u8(&self) -> u8 { 287 | self.0 288 | } 289 | 290 | #[inline] 291 | #[must_use] 292 | pub const fn as_usize(&self) -> usize { 293 | self.0 as usize 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /interpreter/src/runtime.rs: -------------------------------------------------------------------------------- 1 | use alloc::{rc::Rc, vec::Vec}; 2 | 3 | use primitive_types::{H160, H256, U256}; 4 | use sha3::{Digest, Keccak256}; 5 | 6 | use crate::error::ExitError; 7 | 8 | /// Gas state. 9 | pub trait GasState { 10 | fn gas(&self) -> U256; 11 | } 12 | 13 | /// Runtime state. 14 | #[derive(Clone, Debug)] 15 | pub struct RuntimeState { 16 | /// Runtime context. 17 | pub context: Context, 18 | /// Transaction context. 19 | pub transaction_context: Rc, 20 | /// Return data buffer. 21 | pub retbuf: Vec, 22 | } 23 | 24 | impl AsRef for RuntimeState { 25 | fn as_ref(&self) -> &Self { 26 | self 27 | } 28 | } 29 | 30 | impl AsMut for RuntimeState { 31 | fn as_mut(&mut self) -> &mut Self { 32 | self 33 | } 34 | } 35 | 36 | impl GasState for RuntimeState { 37 | fn gas(&self) -> U256 { 38 | U256::zero() 39 | } 40 | } 41 | 42 | /// Context of the runtime. 43 | #[derive(Clone, Debug)] 44 | pub struct Context { 45 | /// Execution address. 46 | pub address: H160, 47 | /// Caller of the EVM. 48 | pub caller: H160, 49 | /// Apparent value of the EVM. 50 | pub apparent_value: U256, 51 | } 52 | 53 | #[derive(Clone, Debug)] 54 | pub struct TransactionContext { 55 | /// Gas price. 56 | pub gas_price: U256, 57 | /// Origin. 58 | pub origin: H160, 59 | } 60 | 61 | /// Transfer from source to target, with given value. 62 | #[derive(Clone, Debug)] 63 | pub struct Transfer { 64 | /// Source address. 65 | pub source: H160, 66 | /// Target address. 67 | pub target: H160, 68 | /// Transfer value. 69 | pub value: U256, 70 | } 71 | 72 | /// Log 73 | #[derive(Clone, Debug)] 74 | pub struct Log { 75 | pub address: H160, 76 | pub topics: Vec, 77 | pub data: Vec, 78 | } 79 | 80 | // Identify if the origin of set_code() comes from a transact or subcall. 81 | #[derive(Clone, Debug)] 82 | pub enum SetCodeOrigin { 83 | Transaction, 84 | Subcall(H160), 85 | } 86 | 87 | #[auto_impl::auto_impl(&, Box)] 88 | pub trait RuntimeEnvironment { 89 | /// Get environmental block hash. 90 | fn block_hash(&self, number: U256) -> H256; 91 | /// Get environmental block number. 92 | fn block_number(&self) -> U256; 93 | /// Get environmental coinbase. 94 | fn block_coinbase(&self) -> H160; 95 | /// Get environmental block timestamp. 96 | fn block_timestamp(&self) -> U256; 97 | /// Get environmental block difficulty. 98 | fn block_difficulty(&self) -> U256; 99 | /// Get environmental block randomness. 100 | fn block_randomness(&self) -> Option; 101 | /// Get environmental gas limit. 102 | fn block_gas_limit(&self) -> U256; 103 | /// Environmental block base fee. 104 | fn block_base_fee_per_gas(&self) -> U256; 105 | /// Get environmental chain ID. 106 | fn chain_id(&self) -> U256; 107 | } 108 | 109 | #[auto_impl::auto_impl(&, Box)] 110 | pub trait RuntimeBaseBackend { 111 | /// Get balance of address. 112 | fn balance(&self, address: H160) -> U256; 113 | /// Get code size of address. 114 | fn code_size(&self, address: H160) -> U256 { 115 | U256::from(self.code(address).len()) 116 | } 117 | /// Get code hash of address. 118 | fn code_hash(&self, address: H160) -> H256 { 119 | H256::from_slice(&Keccak256::digest(&self.code(address)[..])) 120 | } 121 | /// Get code of address. 122 | fn code(&self, address: H160) -> Vec; 123 | /// Get storage value of address at index. 124 | fn storage(&self, address: H160, index: H256) -> H256; 125 | /// Get transient storage value of address at index. 126 | fn transient_storage(&self, address: H160, index: H256) -> H256; 127 | 128 | /// Check whether an address exists. 129 | fn exists(&self, address: H160) -> bool; 130 | 131 | /// Get the current nonce of an account. 132 | fn nonce(&self, address: H160) -> U256; 133 | } 134 | 135 | /// The distinguish between `RuntimeBaseBackend` and `RuntimeBackend` is for the implementation of 136 | /// overlays. 137 | pub trait RuntimeBackend: RuntimeBaseBackend { 138 | /// Get original storage value of address at index. 139 | fn original_storage(&self, address: H160, index: H256) -> H256; 140 | /// Check whether an address has already been deleted. 141 | fn deleted(&self, address: H160) -> bool; 142 | /// Check whether an address has already been created in the transaction. 143 | fn created(&self, address: H160) -> bool; 144 | /// Checks if the address or (address, index) pair has been previously accessed. 145 | fn is_cold(&self, address: H160, index: Option) -> bool; 146 | fn is_hot(&self, address: H160, index: Option) -> bool { 147 | !self.is_cold(address, index) 148 | } 149 | 150 | /// Mark an address or (address, index) pair as hot. 151 | fn mark_hot(&mut self, address: H160, index: Option); 152 | /// Set storage value of address at index. 153 | fn set_storage(&mut self, address: H160, index: H256, value: H256) -> Result<(), ExitError>; 154 | /// Set transient storage value of address at index, transient storage gets discarded after every transaction. (see EIP-1153) 155 | fn set_transient_storage( 156 | &mut self, 157 | address: H160, 158 | index: H256, 159 | value: H256, 160 | ) -> Result<(), ExitError>; 161 | /// Create a log owned by address with given topics and data. 162 | fn log(&mut self, log: Log) -> Result<(), ExitError>; 163 | /// Mark an address to be deleted and its balance to be reset. 164 | fn mark_delete_reset(&mut self, address: H160); 165 | // Mark an address as created in the current transaction. 166 | fn mark_create(&mut self, address: H160); 167 | /// Fully delete storages of an account. 168 | fn reset_storage(&mut self, address: H160); 169 | /// Set code of an account. 170 | fn set_code( 171 | &mut self, 172 | address: H160, 173 | code: Vec, 174 | origin: SetCodeOrigin, 175 | ) -> Result<(), ExitError>; 176 | /// Reset balance of an account. 177 | fn reset_balance(&mut self, address: H160); 178 | fn deposit(&mut self, target: H160, value: U256); 179 | fn withdrawal(&mut self, source: H160, value: U256) -> Result<(), ExitError>; 180 | /// Initiate a transfer. 181 | fn transfer(&mut self, transfer: Transfer) -> Result<(), ExitError> { 182 | self.withdrawal(transfer.source, transfer.value)?; 183 | self.deposit(transfer.target, transfer.value); 184 | Ok(()) 185 | } 186 | /// Increase the nonce value. 187 | fn inc_nonce(&mut self, address: H160) -> Result<(), ExitError>; 188 | } 189 | -------------------------------------------------------------------------------- /interpreter/src/utils.rs: -------------------------------------------------------------------------------- 1 | //! Small utilities. 2 | 3 | use core::{ 4 | cmp::Ordering, 5 | ops::{Div, Rem}, 6 | }; 7 | 8 | use primitive_types::{H160, H256, U256}; 9 | 10 | use crate::error::{ExitError, ExitFatal}; 11 | 12 | /// Convert [U256] into [H256]. 13 | #[must_use] 14 | pub fn u256_to_h256(v: U256) -> H256 { 15 | let mut r = H256::default(); 16 | v.to_big_endian(&mut r[..]); 17 | r 18 | } 19 | 20 | /// Convert [H256] to [U256]. 21 | #[must_use] 22 | pub fn h256_to_u256(v: H256) -> U256 { 23 | U256::from_big_endian(&v[..]) 24 | } 25 | 26 | /// Convert [U256] into [H160] 27 | #[must_use] 28 | pub fn u256_to_h160(v: U256) -> H160 { 29 | u256_to_h256(v).into() 30 | } 31 | 32 | /// Convert [U256] to [usize]. 33 | pub fn u256_to_usize(v: U256) -> Result { 34 | if v > U256::from(usize::MAX) { 35 | return Err(ExitFatal::NotSupported.into()); 36 | } 37 | Ok(v.as_usize()) 38 | } 39 | 40 | /// Sign of [I256]. 41 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] 42 | pub enum Sign { 43 | Plus, 44 | Minus, 45 | Zero, 46 | } 47 | 48 | const SIGN_BIT_MASK: U256 = U256([ 49 | 0xffff_ffff_ffff_ffff, 50 | 0xffff_ffff_ffff_ffff, 51 | 0xffff_ffff_ffff_ffff, 52 | 0x7fff_ffff_ffff_ffff, 53 | ]); 54 | 55 | /// Signed 256-bit integer. 56 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] 57 | pub struct I256(pub Sign, pub U256); 58 | 59 | impl I256 { 60 | /// Zero value of I256. 61 | #[must_use] 62 | pub const fn zero() -> I256 { 63 | I256(Sign::Zero, U256::zero()) 64 | } 65 | /// Minimum value of I256. 66 | #[must_use] 67 | pub fn min_value() -> I256 { 68 | I256(Sign::Minus, (U256::MAX & SIGN_BIT_MASK) + U256::from(1u64)) 69 | } 70 | } 71 | 72 | impl Ord for I256 { 73 | fn cmp(&self, other: &I256) -> Ordering { 74 | match (self.0, other.0) { 75 | (Sign::Zero, Sign::Zero) => Ordering::Equal, 76 | (Sign::Zero, Sign::Plus) => Ordering::Less, 77 | (Sign::Zero, Sign::Minus) => Ordering::Greater, 78 | (Sign::Minus, Sign::Zero) => Ordering::Less, 79 | (Sign::Minus, Sign::Plus) => Ordering::Less, 80 | (Sign::Minus, Sign::Minus) => self.1.cmp(&other.1).reverse(), 81 | (Sign::Plus, Sign::Minus) => Ordering::Greater, 82 | (Sign::Plus, Sign::Zero) => Ordering::Greater, 83 | (Sign::Plus, Sign::Plus) => self.1.cmp(&other.1), 84 | } 85 | } 86 | } 87 | 88 | impl PartialOrd for I256 { 89 | fn partial_cmp(&self, other: &I256) -> Option { 90 | Some(self.cmp(other)) 91 | } 92 | } 93 | 94 | impl Default for I256 { 95 | fn default() -> I256 { 96 | I256::zero() 97 | } 98 | } 99 | 100 | impl From for I256 { 101 | fn from(val: U256) -> I256 { 102 | if val == U256::zero() { 103 | I256::zero() 104 | } else if val & SIGN_BIT_MASK == val { 105 | I256(Sign::Plus, val) 106 | } else { 107 | I256(Sign::Minus, !val + U256::from(1u64)) 108 | } 109 | } 110 | } 111 | 112 | impl From for U256 { 113 | fn from(value: I256) -> U256 { 114 | let sign = value.0; 115 | if sign == Sign::Zero { 116 | U256::zero() 117 | } else if sign == Sign::Plus { 118 | value.1 119 | } else { 120 | !value.1 + U256::from(1u64) 121 | } 122 | } 123 | } 124 | 125 | impl Div for I256 { 126 | type Output = I256; 127 | 128 | fn div(self, other: I256) -> I256 { 129 | if other == I256::zero() { 130 | return I256::zero(); 131 | } 132 | 133 | if self == I256::min_value() && other.1 == U256::from(1u64) { 134 | return I256::min_value(); 135 | } 136 | 137 | let d = (self.1 / other.1) & SIGN_BIT_MASK; 138 | 139 | if d == U256::zero() { 140 | return I256::zero(); 141 | } 142 | 143 | match (self.0, other.0) { 144 | (Sign::Zero, Sign::Plus) 145 | | (Sign::Plus, Sign::Zero) 146 | | (Sign::Zero, Sign::Zero) 147 | | (Sign::Plus, Sign::Plus) 148 | | (Sign::Minus, Sign::Minus) => I256(Sign::Plus, d), 149 | (Sign::Zero, Sign::Minus) 150 | | (Sign::Plus, Sign::Minus) 151 | | (Sign::Minus, Sign::Zero) 152 | | (Sign::Minus, Sign::Plus) => I256(Sign::Minus, d), 153 | } 154 | } 155 | } 156 | 157 | impl Rem for I256 { 158 | type Output = I256; 159 | 160 | fn rem(self, other: I256) -> I256 { 161 | let r = (self.1 % other.1) & SIGN_BIT_MASK; 162 | 163 | if r == U256::zero() { 164 | return I256::zero(); 165 | } 166 | 167 | I256(self.0, r) 168 | } 169 | } 170 | 171 | #[cfg(test)] 172 | mod tests { 173 | use std::num::Wrapping; 174 | 175 | use super::*; 176 | 177 | #[test] 178 | fn div_i256() { 179 | // Sanity checks based on i8. Notice that we need to use `Wrapping` here because 180 | // Rust will prevent the overflow by default whereas the EVM does not. 181 | assert_eq!(Wrapping(i8::MIN) / Wrapping(-1), Wrapping(i8::MIN)); 182 | 183 | assert_eq!(100i8 / -1, -100i8); 184 | assert_eq!(100i8 / 2, 50i8); 185 | 186 | // Now the same calculations based on i256 187 | let one = I256(Sign::Zero, U256::from(1)); 188 | let one_hundred = I256(Sign::Zero, U256::from(100)); 189 | let fifty = I256(Sign::Plus, U256::from(50)); 190 | let two = I256(Sign::Zero, U256::from(2)); 191 | let neg_one_hundred = I256(Sign::Minus, U256::from(100)); 192 | let minus_one = I256(Sign::Minus, U256::from(1)); 193 | let max_value = I256(Sign::Plus, U256::from(2).pow(U256::from(255)) - 1); 194 | let neg_max_value = I256(Sign::Minus, U256::from(2).pow(U256::from(255)) - 1); 195 | 196 | assert_eq!(I256::min_value() / minus_one, I256::min_value()); 197 | assert_eq!(I256::min_value() / one, I256::min_value()); 198 | assert_eq!(max_value / one, max_value); 199 | assert_eq!(max_value / minus_one, neg_max_value); 200 | 201 | assert_eq!(one_hundred / minus_one, neg_one_hundred); 202 | assert_eq!(one_hundred / two, fifty); 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /interpreter/tests/performance.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | use evm_interpreter::{ 4 | error::{Capture, ExitSucceed}, 5 | etable::Etable, 6 | machine::Machine, 7 | EtableInterpreter, RunInterpreter, 8 | }; 9 | 10 | static ETABLE: Etable<(), (), ()> = Etable::core(); 11 | 12 | macro_rules! ret_test { 13 | ($name:ident, $code:expr, $data:expr, $ret:expr) => { 14 | #[test] 15 | fn $name() { 16 | let code = hex::decode($code).unwrap(); 17 | let data = hex::decode($data).unwrap(); 18 | 19 | let machine = Machine::new(Rc::new(code), Rc::new(data), 1024, 10000, ()); 20 | let mut vm = EtableInterpreter::new(machine, &ETABLE); 21 | assert_eq!( 22 | vm.run(&mut ()), 23 | Capture::Exit(Ok(ExitSucceed::Returned.into())) 24 | ); 25 | assert_eq!(vm.retval, hex::decode($ret).unwrap()); 26 | } 27 | }; 28 | } 29 | 30 | ret_test!( 31 | ackermann31, 32 | "60e060020a6000350480632839e92814601e57806361047ff414603457005b602a6004356024356047565b8060005260206000f35b603d6004356099565b8060005260206000f35b600082600014605457605e565b8160010190506093565b81600014606957607b565b60756001840360016047565b90506093565b609060018403608c85600186036047565b6047565b90505b92915050565b6000816000148060a95750816001145b60b05760b7565b81905060cf565b60c1600283036099565b60cb600184036099565b0190505b91905056", 33 | "2839e92800000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001", 34 | "000000000000000000000000000000000000000000000000000000000000000d" 35 | ); 36 | 37 | ret_test!( 38 | ackermann32, 39 | "60e060020a6000350480632839e92814601e57806361047ff414603457005b602a6004356024356047565b8060005260206000f35b603d6004356099565b8060005260206000f35b600082600014605457605e565b8160010190506093565b81600014606957607b565b60756001840360016047565b90506093565b609060018403608c85600186036047565b6047565b90505b92915050565b6000816000148060a95750816001145b60b05760b7565b81905060cf565b60c1600283036099565b60cb600184036099565b0190505b91905056", 40 | "2839e92800000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000002", 41 | "000000000000000000000000000000000000000000000000000000000000001d" 42 | ); 43 | 44 | ret_test!( 45 | fibonacci10, 46 | "60e060020a6000350480632839e92814601e57806361047ff414603457005b602a6004356024356047565b8060005260206000f35b603d6004356099565b8060005260206000f35b600082600014605457605e565b8160010190506093565b81600014606957607b565b60756001840360016047565b90506093565b609060018403608c85600186036047565b6047565b90505b92915050565b6000816000148060a95750816001145b60b05760b7565b81905060cf565b60c1600283036099565b60cb600184036099565b0190505b91905056", 47 | "61047ff4000000000000000000000000000000000000000000000000000000000000000a", 48 | "0000000000000000000000000000000000000000000000000000000000000037" 49 | ); 50 | 51 | ret_test!( 52 | fibonacci16, 53 | "60e060020a6000350480632839e92814601e57806361047ff414603457005b602a6004356024356047565b8060005260206000f35b603d6004356099565b8060005260206000f35b600082600014605457605e565b8160010190506093565b81600014606957607b565b60756001840360016047565b90506093565b609060018403608c85600186036047565b6047565b90505b92915050565b6000816000148060a95750816001145b60b05760b7565b81905060cf565b60c1600283036099565b60cb600184036099565b0190505b91905056", 54 | "61047ff40000000000000000000000000000000000000000000000000000000000000010", 55 | "00000000000000000000000000000000000000000000000000000000000003db" 56 | ); 57 | -------------------------------------------------------------------------------- /interpreter/tests/usability.rs: -------------------------------------------------------------------------------- 1 | use evm_interpreter::{ 2 | error::{CallCreateTrap, Capture, ExitError, ExitSucceed}, 3 | etable::{Control, Etable, MultiEfn, MultiEtable}, 4 | machine::Machine, 5 | opcode::Opcode, 6 | runtime::{ 7 | Context, Log, RuntimeBackend, RuntimeBaseBackend, RuntimeEnvironment, RuntimeState, 8 | SetCodeOrigin, TransactionContext, 9 | }, 10 | EtableInterpreter, RunInterpreter, 11 | }; 12 | use primitive_types::{H160, H256, U256}; 13 | use std::rc::Rc; 14 | 15 | const CODE1: &str = "60e060020a6000350480632839e92814601e57806361047ff414603457005b602a6004356024356047565b8060005260206000f35b603d6004356099565b8060005260206000f35b600082600014605457605e565b8160010190506093565b81600014606957607b565b60756001840360016047565b90506093565b609060018403608c85600186036047565b6047565b90505b92915050565b6000816000148060a95750816001145b60b05760b7565b81905060cf565b60c1600283036099565b60cb600184036099565b0190505b91905056"; 16 | const DATA1: &str = "2839e92800000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001"; 17 | const RET1: &str = "000000000000000000000000000000000000000000000000000000000000000d"; 18 | 19 | #[test] 20 | fn etable_wrap() { 21 | let code = hex::decode(CODE1).unwrap(); 22 | let data = hex::decode(DATA1).unwrap(); 23 | 24 | let wrapped_etable = Etable::<_, _, Opcode>::core().wrap(|f, opcode_t| { 25 | move |machine, handle, position| { 26 | let opcode = Opcode(machine.code()[position]); 27 | assert_eq!(opcode_t, opcode); 28 | println!("opcode: {:?}", opcode); 29 | f(machine, handle, position) 30 | } 31 | }); 32 | 33 | let machine = Machine::new(Rc::new(code), Rc::new(data), 1024, 10000, ()); 34 | let mut vm = EtableInterpreter::new(machine, &wrapped_etable); 35 | let result = vm.run(&mut ()); 36 | assert_eq!(result, Capture::Exit(Ok(ExitSucceed::Returned))); 37 | assert_eq!(vm.retval, hex::decode(RET1).unwrap()); 38 | } 39 | 40 | #[test] 41 | #[allow(clippy::type_complexity)] 42 | fn etable_wrap2() { 43 | let code = hex::decode(CODE1).unwrap(); 44 | let data = hex::decode(DATA1).unwrap(); 45 | 46 | let wrapped_etable = Etable::core().wrap( 47 | |f, opcode_t| -> Box, &mut (), usize) -> Control> { 48 | if opcode_t != Opcode(0x50) { 49 | Box::new(move |machine, handle, position| { 50 | let opcode = Opcode(machine.code()[position]); 51 | assert_eq!(opcode_t, opcode); 52 | println!("opcode: {:?}", opcode); 53 | f(machine, handle, position) 54 | }) 55 | } else { 56 | Box::new(|machine, _handle, position| { 57 | let opcode = Opcode(machine.code()[position]); 58 | println!("disabled!"); 59 | Control::Trap(opcode) 60 | }) 61 | } 62 | }, 63 | ); 64 | 65 | let machine = Machine::new(Rc::new(code), Rc::new(data), 1024, 10000, ()); 66 | let mut vm = EtableInterpreter::new(machine, &wrapped_etable); 67 | let result = vm.run(&mut ()); 68 | assert_eq!(result, Capture::Trap(Opcode(0x50))); 69 | } 70 | 71 | pub struct UnimplementedHandler; 72 | 73 | impl RuntimeEnvironment for UnimplementedHandler { 74 | fn block_hash(&self, _number: U256) -> H256 { 75 | unimplemented!() 76 | } 77 | fn block_number(&self) -> U256 { 78 | unimplemented!() 79 | } 80 | fn block_coinbase(&self) -> H160 { 81 | unimplemented!() 82 | } 83 | fn block_timestamp(&self) -> U256 { 84 | unimplemented!() 85 | } 86 | fn block_difficulty(&self) -> U256 { 87 | unimplemented!() 88 | } 89 | fn block_randomness(&self) -> Option { 90 | unimplemented!() 91 | } 92 | fn block_gas_limit(&self) -> U256 { 93 | unimplemented!() 94 | } 95 | fn block_base_fee_per_gas(&self) -> U256 { 96 | unimplemented!() 97 | } 98 | fn chain_id(&self) -> U256 { 99 | unimplemented!() 100 | } 101 | } 102 | 103 | impl RuntimeBaseBackend for UnimplementedHandler { 104 | fn balance(&self, _address: H160) -> U256 { 105 | unimplemented!() 106 | } 107 | fn code_size(&self, _address: H160) -> U256 { 108 | unimplemented!() 109 | } 110 | fn code_hash(&self, _address: H160) -> H256 { 111 | unimplemented!() 112 | } 113 | fn code(&self, _address: H160) -> Vec { 114 | unimplemented!() 115 | } 116 | fn storage(&self, _address: H160, _index: H256) -> H256 { 117 | unimplemented!() 118 | } 119 | fn transient_storage(&self, _address: H160, _index: H256) -> H256 { 120 | unimplemented!() 121 | } 122 | 123 | fn exists(&self, _address: H160) -> bool { 124 | unimplemented!() 125 | } 126 | 127 | fn nonce(&self, _address: H160) -> U256 { 128 | unimplemented!() 129 | } 130 | } 131 | 132 | impl RuntimeBackend for UnimplementedHandler { 133 | fn original_storage(&self, _address: H160, _index: H256) -> H256 { 134 | unimplemented!() 135 | } 136 | 137 | fn deleted(&self, _address: H160) -> bool { 138 | unimplemented!() 139 | } 140 | 141 | fn created(&self, _address: H160) -> bool { 142 | unimplemented!() 143 | } 144 | 145 | fn is_cold(&self, _address: H160, _index: Option) -> bool { 146 | unimplemented!() 147 | } 148 | 149 | fn mark_hot(&mut self, _address: H160, _index: Option) { 150 | unimplemented!() 151 | } 152 | 153 | fn set_storage(&mut self, _address: H160, _index: H256, _value: H256) -> Result<(), ExitError> { 154 | unimplemented!() 155 | } 156 | fn set_transient_storage( 157 | &mut self, 158 | _address: H160, 159 | _index: H256, 160 | _value: H256, 161 | ) -> Result<(), ExitError> { 162 | unimplemented!() 163 | } 164 | fn log(&mut self, _log: Log) -> Result<(), ExitError> { 165 | unimplemented!() 166 | } 167 | fn mark_delete_reset(&mut self, _address: H160) { 168 | unimplemented!() 169 | } 170 | 171 | fn mark_create(&mut self, _address: H160) { 172 | unimplemented!() 173 | } 174 | 175 | fn reset_storage(&mut self, _address: H160) { 176 | unimplemented!() 177 | } 178 | 179 | fn set_code( 180 | &mut self, 181 | _address: H160, 182 | _code: Vec, 183 | _origin: SetCodeOrigin, 184 | ) -> Result<(), ExitError> { 185 | unimplemented!() 186 | } 187 | 188 | fn reset_balance(&mut self, _address: H160) { 189 | unimplemented!() 190 | } 191 | 192 | fn deposit(&mut self, _address: H160, _value: U256) { 193 | unimplemented!() 194 | } 195 | fn withdrawal(&mut self, _address: H160, _value: U256) -> Result<(), ExitError> { 196 | unimplemented!() 197 | } 198 | 199 | fn inc_nonce(&mut self, _address: H160) -> Result<(), ExitError> { 200 | unimplemented!() 201 | } 202 | } 203 | 204 | static RUNTIME_ETABLE: Etable = 205 | Etable::runtime(); 206 | 207 | #[test] 208 | fn etable_runtime() { 209 | let code = hex::decode(CODE1).unwrap(); 210 | let data = hex::decode(DATA1).unwrap(); 211 | let mut handler = UnimplementedHandler; 212 | 213 | let machine = Machine::new( 214 | Rc::new(code), 215 | Rc::new(data), 216 | 1024, 217 | 10000, 218 | RuntimeState { 219 | context: Context { 220 | address: H160::default(), 221 | caller: H160::default(), 222 | apparent_value: U256::default(), 223 | }, 224 | transaction_context: TransactionContext { 225 | gas_price: U256::default(), 226 | origin: H160::default(), 227 | } 228 | .into(), 229 | retbuf: Vec::new(), 230 | }, 231 | ); 232 | let mut vm = EtableInterpreter::new(machine, &RUNTIME_ETABLE); 233 | 234 | let res = vm.run(&mut handler).exit().unwrap(); 235 | assert_eq!(res, Ok(ExitSucceed::Returned)); 236 | assert_eq!(vm.retval, hex::decode(RET1).unwrap()); 237 | } 238 | 239 | #[test] 240 | fn etable_multi() { 241 | let prefix = Opcode(0xc1); 242 | let orig_code = hex::decode("600d60005260206000f3").unwrap(); 243 | let mut code = Vec::new(); 244 | let mut skip = 0; 245 | for c in orig_code { 246 | if skip > 0 { 247 | code.push(c); 248 | skip -= 1; 249 | } else { 250 | code.push(prefix.0); 251 | code.push(c); 252 | if let Some(p) = Opcode(c).is_push() { 253 | skip += p as usize; 254 | } 255 | } 256 | } 257 | 258 | let data = hex::decode(DATA1).unwrap(); 259 | let mut handler = UnimplementedHandler; 260 | 261 | let etable: Etable = Etable::runtime(); 262 | let mut multi_etable: MultiEtable = 263 | etable.into(); 264 | let prefix_etable = 265 | MultiEtable::from(Etable::::runtime()); 266 | multi_etable[prefix.as_usize()] = MultiEfn::Node(Box::new(prefix_etable)); 267 | 268 | let machine = Machine::new( 269 | Rc::new(code), 270 | Rc::new(data), 271 | 1024, 272 | 10000, 273 | RuntimeState { 274 | context: Context { 275 | address: H160::default(), 276 | caller: H160::default(), 277 | apparent_value: U256::default(), 278 | }, 279 | transaction_context: TransactionContext { 280 | gas_price: U256::default(), 281 | origin: H160::default(), 282 | } 283 | .into(), 284 | retbuf: Vec::new(), 285 | }, 286 | ); 287 | let mut vm = EtableInterpreter::new(machine, &multi_etable); 288 | 289 | let res = vm.run(&mut handler).exit().unwrap(); 290 | assert_eq!(res, Ok(ExitSucceed::Returned)); 291 | assert_eq!(vm.retval, hex::decode(RET1).unwrap()); 292 | } 293 | -------------------------------------------------------------------------------- /jsontests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "jsontests" 3 | version = "0.0.0-dev" 4 | edition = { workspace = true } 5 | rust-version = { workspace = true } 6 | license = { workspace = true } 7 | authors = { workspace = true } 8 | repository = { workspace = true } 9 | keywords = { workspace = true } 10 | 11 | [dependencies] 12 | clap = { version = "4", features = ["derive"] } 13 | ethereum = "0.15.0" 14 | evm = { path = ".." } 15 | evm-precompile = { path = "../precompile" } 16 | hex = { version = "0.4", features = ["serde"] } 17 | primitive-types = { version = "0.12", features = ["rlp", "serde"] } 18 | rlp = "0.5" 19 | serde = { version = "1", features = ["derive"] } 20 | serde_json = "1" 21 | sha3 = "0.10" 22 | thiserror = "1" 23 | -------------------------------------------------------------------------------- /jsontests/src/error.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, thiserror::Error)] 2 | #[allow(dead_code)] 3 | pub enum TestError { 4 | #[error("state root is different")] 5 | StateMismatch, 6 | #[error("expect error, but got okay")] 7 | ExpectException, 8 | } 9 | 10 | #[allow(clippy::upper_case_acronyms)] 11 | #[derive(Debug, thiserror::Error)] 12 | pub enum Error { 13 | #[error("io error")] 14 | IO(#[from] std::io::Error), 15 | #[error("json error")] 16 | JSON(#[from] serde_json::Error), 17 | #[error("evm error")] 18 | EVM(#[from] evm::interpreter::error::ExitError), 19 | #[error("unsupported fork")] 20 | UnsupportedFork, 21 | #[error("non-utf8 filename")] 22 | NonUtf8Filename, 23 | #[error("test error")] 24 | Test(#[from] TestError), 25 | } 26 | -------------------------------------------------------------------------------- /jsontests/src/hash.rs: -------------------------------------------------------------------------------- 1 | use evm::interpreter::utils::h256_to_u256; 2 | use primitive_types::{H256, U256}; 3 | use sha3::{Digest, Keccak256}; 4 | 5 | use crate::in_memory::InMemoryBackend; 6 | 7 | /// Basic account type. 8 | #[derive(Debug, Clone, PartialEq, Eq)] 9 | pub struct TrieAccount { 10 | /// Nonce of the account. 11 | pub nonce: U256, 12 | /// Balance of the account. 13 | pub balance: U256, 14 | /// Storage root of the account. 15 | pub storage_root: H256, 16 | /// Code hash of the account. 17 | pub code_hash: H256, 18 | /// Code version of the account. 19 | pub code_version: U256, 20 | } 21 | 22 | impl rlp::Encodable for TrieAccount { 23 | fn rlp_append(&self, stream: &mut rlp::RlpStream) { 24 | let use_short_version = self.code_version == U256::zero(); 25 | 26 | match use_short_version { 27 | true => { 28 | stream.begin_list(4); 29 | } 30 | false => { 31 | stream.begin_list(5); 32 | } 33 | } 34 | 35 | stream.append(&self.nonce); 36 | stream.append(&self.balance); 37 | stream.append(&self.storage_root); 38 | stream.append(&self.code_hash); 39 | 40 | if !use_short_version { 41 | stream.append(&self.code_version); 42 | } 43 | } 44 | } 45 | 46 | impl rlp::Decodable for TrieAccount { 47 | fn decode(rlp: &rlp::Rlp) -> Result { 48 | let use_short_version = match rlp.item_count()? { 49 | 4 => true, 50 | 5 => false, 51 | _ => return Err(rlp::DecoderError::RlpIncorrectListLen), 52 | }; 53 | 54 | Ok(TrieAccount { 55 | nonce: rlp.val_at(0)?, 56 | balance: rlp.val_at(1)?, 57 | storage_root: rlp.val_at(2)?, 58 | code_hash: rlp.val_at(3)?, 59 | code_version: if use_short_version { 60 | U256::zero() 61 | } else { 62 | rlp.val_at(4)? 63 | }, 64 | }) 65 | } 66 | } 67 | 68 | pub fn state_root(backend: &InMemoryBackend) -> H256 { 69 | let tree = backend 70 | .state 71 | .iter() 72 | .map(|(address, account)| { 73 | let storage_root = ethereum::util::sec_trie_root( 74 | account 75 | .storage 76 | .iter() 77 | .map(|(k, v)| (k, rlp::encode(&h256_to_u256(*v)))), 78 | ); 79 | 80 | let code_hash = H256::from_slice(&Keccak256::digest(&account.code)); 81 | let account = TrieAccount { 82 | nonce: account.nonce, 83 | balance: account.balance, 84 | storage_root, 85 | code_hash, 86 | code_version: U256::zero(), 87 | }; 88 | 89 | (address, rlp::encode(&account)) 90 | }) 91 | .collect::>(); 92 | 93 | ethereum::util::sec_trie_root(tree) 94 | } 95 | -------------------------------------------------------------------------------- /jsontests/src/in_memory.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | 3 | use evm::{ 4 | backend::OverlayedChangeSet, 5 | interpreter::runtime::{RuntimeBaseBackend, RuntimeEnvironment}, 6 | }; 7 | use primitive_types::{H160, H256, U256}; 8 | 9 | #[derive(Clone, Debug)] 10 | pub struct InMemoryEnvironment { 11 | pub block_hashes: BTreeMap, 12 | pub block_number: U256, 13 | pub block_coinbase: H160, 14 | pub block_timestamp: U256, 15 | pub block_difficulty: U256, 16 | pub block_randomness: Option, 17 | pub block_gas_limit: U256, 18 | pub block_base_fee_per_gas: U256, 19 | pub chain_id: U256, 20 | } 21 | 22 | #[derive(Clone, Debug, Default)] 23 | pub struct InMemoryAccount { 24 | pub balance: U256, 25 | pub code: Vec, 26 | pub nonce: U256, 27 | pub storage: BTreeMap, 28 | pub transient_storage: BTreeMap, 29 | } 30 | 31 | #[derive(Clone, Debug)] 32 | #[allow(dead_code)] 33 | pub struct InMemorySuicideInfo { 34 | pub address: H160, 35 | } 36 | 37 | #[derive(Clone, Debug)] 38 | pub struct InMemoryBackend { 39 | pub environment: InMemoryEnvironment, 40 | pub state: BTreeMap, 41 | } 42 | 43 | impl InMemoryBackend { 44 | pub fn apply_overlayed(&mut self, changeset: &OverlayedChangeSet) { 45 | for (address, balance) in changeset.balances.clone() { 46 | self.state.entry(address).or_default().balance = balance; 47 | } 48 | 49 | for (address, code) in changeset.codes.clone() { 50 | self.state.entry(address).or_default().code = code; 51 | } 52 | 53 | for (address, nonce) in changeset.nonces.clone() { 54 | self.state.entry(address).or_default().nonce = nonce; 55 | } 56 | 57 | for address in changeset.storage_resets.clone() { 58 | self.state.entry(address).or_default().storage = BTreeMap::new(); 59 | } 60 | 61 | for ((address, key), value) in changeset.storages.clone() { 62 | let account = self.state.entry(address).or_default(); 63 | 64 | if value == H256::default() { 65 | account.storage.remove(&key); 66 | } else { 67 | account.storage.insert(key, value); 68 | } 69 | } 70 | 71 | for ((address, key), value) in changeset.transient_storage.clone() { 72 | let account = self.state.entry(address).or_default(); 73 | 74 | if value == H256::default() { 75 | account.transient_storage.remove(&key); 76 | } else { 77 | account.transient_storage.insert(key, value); 78 | } 79 | } 80 | 81 | for address in changeset.deletes.clone() { 82 | self.state.remove(&address); 83 | } 84 | } 85 | } 86 | 87 | impl RuntimeEnvironment for InMemoryBackend { 88 | fn block_hash(&self, number: U256) -> H256 { 89 | self.environment 90 | .block_hashes 91 | .get(&number) 92 | .cloned() 93 | .unwrap_or(H256::default()) 94 | } 95 | 96 | fn block_number(&self) -> U256 { 97 | self.environment.block_number 98 | } 99 | 100 | fn block_coinbase(&self) -> H160 { 101 | self.environment.block_coinbase 102 | } 103 | 104 | fn block_timestamp(&self) -> U256 { 105 | self.environment.block_timestamp 106 | } 107 | 108 | fn block_difficulty(&self) -> U256 { 109 | self.environment.block_difficulty 110 | } 111 | 112 | fn block_randomness(&self) -> Option { 113 | self.environment.block_randomness 114 | } 115 | 116 | fn block_gas_limit(&self) -> U256 { 117 | self.environment.block_gas_limit 118 | } 119 | 120 | fn block_base_fee_per_gas(&self) -> U256 { 121 | self.environment.block_base_fee_per_gas 122 | } 123 | 124 | fn chain_id(&self) -> U256 { 125 | self.environment.chain_id 126 | } 127 | } 128 | 129 | impl RuntimeBaseBackend for InMemoryBackend { 130 | fn balance(&self, address: H160) -> U256 { 131 | self.state 132 | .get(&address) 133 | .cloned() 134 | .unwrap_or(Default::default()) 135 | .balance 136 | } 137 | 138 | fn code(&self, address: H160) -> Vec { 139 | self.state 140 | .get(&address) 141 | .cloned() 142 | .unwrap_or(Default::default()) 143 | .code 144 | } 145 | 146 | fn exists(&self, address: H160) -> bool { 147 | self.state.contains_key(&address) 148 | } 149 | 150 | fn storage(&self, address: H160, index: H256) -> H256 { 151 | self.state 152 | .get(&address) 153 | .cloned() 154 | .unwrap_or(Default::default()) 155 | .storage 156 | .get(&index) 157 | .cloned() 158 | .unwrap_or(H256::default()) 159 | } 160 | 161 | fn transient_storage(&self, address: H160, index: H256) -> H256 { 162 | self.state 163 | .get(&address) 164 | .cloned() 165 | .unwrap_or(Default::default()) 166 | .transient_storage 167 | .get(&index) 168 | .cloned() 169 | .unwrap_or(H256::default()) 170 | } 171 | 172 | fn nonce(&self, address: H160) -> U256 { 173 | self.state 174 | .get(&address) 175 | .cloned() 176 | .unwrap_or(Default::default()) 177 | .nonce 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /jsontests/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod error; 2 | pub mod hash; 3 | pub mod in_memory; 4 | pub mod run; 5 | pub mod types; 6 | 7 | #[test] 8 | fn st_args_zero_one_balance() { 9 | const JSON_FILENAME: &str = "res/ethtests/GeneralStateTests/stArgsZeroOneBalance/"; 10 | let tests_status = run::run_single(JSON_FILENAME, false).unwrap(); 11 | tests_status.print_total(); 12 | } 13 | 14 | #[test] 15 | fn st_code_copy_test() { 16 | const JSON_FILENAME: &str = "res/ethtests/GeneralStateTests/stCodeCopyTest/"; 17 | let tests_status = run::run_single(JSON_FILENAME, false).unwrap(); 18 | tests_status.print_total(); 19 | } 20 | 21 | #[test] 22 | fn st_example() { 23 | const JSON_FILENAME: &str = "res/ethtests/GeneralStateTests/stExample/"; 24 | let tests_status = run::run_single(JSON_FILENAME, false).unwrap(); 25 | tests_status.print_total(); 26 | } 27 | 28 | #[test] 29 | fn st_self_balance() { 30 | const JSON_FILENAME: &str = "res/ethtests/GeneralStateTests/stSelfBalance/"; 31 | let tests_status = run::run_single(JSON_FILENAME, false).unwrap(); 32 | tests_status.print_total(); 33 | } 34 | 35 | #[test] 36 | fn st_s_load_test() { 37 | const JSON_FILENAME: &str = "res/ethtests/GeneralStateTests/stSLoadTest/"; 38 | let tests_status = run::run_single(JSON_FILENAME, false).unwrap(); 39 | tests_status.print_total(); 40 | } 41 | 42 | #[test] 43 | fn vm_arithmetic_test() { 44 | const JSON_FILENAME: &str = "res/ethtests/GeneralStateTests/VMTests/vmArithmeticTest/"; 45 | let tests_status = run::run_single(JSON_FILENAME, false).unwrap(); 46 | tests_status.print_total(); 47 | } 48 | 49 | #[test] 50 | fn vm_bitwise_logic_operation() { 51 | const JSON_FILENAME: &str = "res/ethtests/GeneralStateTests/VMTests/vmBitwiseLogicOperation/"; 52 | let tests_status = run::run_single(JSON_FILENAME, false).unwrap(); 53 | tests_status.print_total(); 54 | } 55 | 56 | #[test] 57 | fn vm_io_and_flow_operations() { 58 | const JSON_FILENAME: &str = "res/ethtests/GeneralStateTests/VMTests/vmIOandFlowOperations/"; 59 | let tests_status = run::run_single(JSON_FILENAME, false).unwrap(); 60 | tests_status.print_total(); 61 | } 62 | 63 | #[test] 64 | fn vm_log_test() { 65 | const JSON_FILENAME: &str = "res/ethtests/GeneralStateTests/VMTests/vmLogTest/"; 66 | let tests_status = run::run_single(JSON_FILENAME, false).unwrap(); 67 | tests_status.print_total(); 68 | } 69 | 70 | #[test] 71 | fn vm_tests() { 72 | const JSON_FILENAME: &str = "res/ethtests/GeneralStateTests/VMTests/vmTests/"; 73 | let tests_status = run::run_single(JSON_FILENAME, false).unwrap(); 74 | tests_status.print_total(); 75 | } 76 | 77 | #[test] 78 | fn sqt_eip_2930() { 79 | const JSON_FILENAME: &str = 80 | "res/ethtests/GeneralStateTests/stEIP150singleCodeGasPrices/eip2929.json"; 81 | let tests_status = run::run_single(JSON_FILENAME, false).unwrap(); 82 | tests_status.print_total(); 83 | } 84 | -------------------------------------------------------------------------------- /jsontests/src/main.rs: -------------------------------------------------------------------------------- 1 | mod error; 2 | mod hash; 3 | mod in_memory; 4 | mod run; 5 | mod types; 6 | 7 | use clap::Parser; 8 | 9 | use crate::{error::Error, types::*}; 10 | 11 | #[derive(Parser)] 12 | #[command(author, version, about, long_about = None)] 13 | struct Cli { 14 | filenames: Vec, 15 | 16 | #[arg(short, long, default_value_t = false)] 17 | debug: bool, 18 | } 19 | 20 | fn main() -> Result<(), Error> { 21 | let cli = Cli::parse(); 22 | 23 | let mut tests_status = TestCompletionStatus::default(); 24 | for filename in cli.filenames { 25 | tests_status += run::run_single(&filename, cli.debug)?; 26 | } 27 | tests_status.print_total(); 28 | 29 | Ok(()) 30 | } 31 | -------------------------------------------------------------------------------- /jsontests/src/run.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | collections::{BTreeMap, BTreeSet}, 3 | fs::{self, File}, 4 | io::BufReader, 5 | }; 6 | 7 | use evm::{ 8 | backend::OverlayedBackend, 9 | interpreter::{ 10 | error::Capture, 11 | etable::{Chained, Single}, 12 | runtime::GasState, 13 | utils::u256_to_h256, 14 | Interpreter, 15 | }, 16 | standard::{Config, Etable, EtableResolver, Invoker, TransactArgs}, 17 | }; 18 | use evm_precompile::StandardPrecompileSet; 19 | use primitive_types::U256; 20 | 21 | use crate::{ 22 | error::{Error, TestError}, 23 | in_memory::{InMemoryAccount, InMemoryBackend, InMemoryEnvironment}, 24 | types::{Fork, TestCompletionStatus, TestData, TestExpectException, TestMulti}, 25 | }; 26 | 27 | const BASIC_FILE_PATH_TO_TRIM: [&str; 2] = [ 28 | "jsontests/res/ethtests/GeneralStateTests/", 29 | "res/ethtests/GeneralStateTests/", 30 | ]; 31 | 32 | fn get_short_file_name(filename: &str) -> String { 33 | let mut short_file_name = String::from(filename); 34 | for pattern in BASIC_FILE_PATH_TO_TRIM { 35 | short_file_name = short_file_name.replace(pattern, ""); 36 | } 37 | short_file_name.clone().to_string() 38 | } 39 | 40 | /// Run tests for specific json file with debug flag 41 | fn run_file(filename: &str, debug: bool) -> Result { 42 | let test_multi: BTreeMap = 43 | serde_json::from_reader(BufReader::new(File::open(filename)?))?; 44 | let mut tests_status = TestCompletionStatus::default(); 45 | 46 | for (test_name, test_multi) in test_multi { 47 | let tests = test_multi.tests(); 48 | let short_file_name = get_short_file_name(filename); 49 | for test in &tests { 50 | if debug { 51 | print!( 52 | "[{:?}] {} | {}/{} DEBUG: ", 53 | test.fork, short_file_name, test_name, test.index 54 | ); 55 | } else { 56 | print!( 57 | "[{:?}] {} | {}/{}: ", 58 | test.fork, short_file_name, test_name, test.index 59 | ); 60 | } 61 | match run_test(filename, &test_name, test.clone(), debug) { 62 | Ok(()) => { 63 | tests_status.inc_completed(); 64 | println!("ok") 65 | } 66 | Err(Error::UnsupportedFork) => { 67 | tests_status.inc_skipped(); 68 | println!("skipped") 69 | } 70 | Err(err) => { 71 | println!("ERROR: {:?}", err); 72 | return Err(err); 73 | } 74 | } 75 | if debug { 76 | println!(); 77 | } 78 | } 79 | 80 | tests_status.print_completion(); 81 | } 82 | 83 | Ok(tests_status) 84 | } 85 | 86 | /// Run test for single json file or directory 87 | pub fn run_single(filename: &str, debug: bool) -> Result { 88 | if fs::metadata(filename)?.is_dir() { 89 | let mut tests_status = TestCompletionStatus::default(); 90 | 91 | for filename in fs::read_dir(filename)? { 92 | let filepath = filename?.path(); 93 | let filename = filepath.to_str().ok_or(Error::NonUtf8Filename)?; 94 | println!("RUM for: {filename}"); 95 | tests_status += run_file(filename, debug)?; 96 | } 97 | tests_status.print_total_for_dir(filename); 98 | Ok(tests_status) 99 | } else { 100 | run_file(filename, debug) 101 | } 102 | } 103 | 104 | /// Run single test 105 | pub fn run_test( 106 | _filename: &str, 107 | _test_name: &str, 108 | test: TestData, 109 | debug: bool, 110 | ) -> Result<(), Error> { 111 | let config = match test.fork { 112 | Fork::Berlin => Config::berlin(), 113 | _ => return Err(Error::UnsupportedFork), 114 | }; 115 | 116 | if test.post.expect_exception == Some(TestExpectException::TR_TypeNotSupported) { 117 | // The `evm` crate does not understand transaction format, only the `ethereum` crate. So 118 | // there's nothing for us to test here for `TR_TypeNotSupported`. 119 | return Ok(()); 120 | } 121 | 122 | let env = InMemoryEnvironment { 123 | block_hashes: BTreeMap::new(), // TODO: fill in this field. 124 | block_number: test.env.current_number, 125 | block_coinbase: test.env.current_coinbase, 126 | block_timestamp: test.env.current_timestamp, 127 | block_difficulty: test.env.current_difficulty, 128 | block_randomness: Some(test.env.current_random), 129 | block_gas_limit: test.env.current_gas_limit, 130 | block_base_fee_per_gas: test.transaction.gas_price, 131 | chain_id: U256::zero(), // TODO: fill in this field. 132 | }; 133 | 134 | let state = test 135 | .pre 136 | .clone() 137 | .into_iter() 138 | .map(|(address, account)| { 139 | let storage = account 140 | .storage 141 | .into_iter() 142 | .filter(|(_, value)| *value != U256::zero()) 143 | .map(|(key, value)| (u256_to_h256(key), u256_to_h256(value))) 144 | .collect::>(); 145 | 146 | ( 147 | address, 148 | InMemoryAccount { 149 | balance: account.balance, 150 | code: account.code.0, 151 | nonce: account.nonce, 152 | storage, 153 | transient_storage: Default::default(), 154 | }, 155 | ) 156 | }) 157 | .collect::>(); 158 | 159 | let gas_etable = Single::new(evm::standard::eval_gasometer); 160 | let exec_etable = Etable::runtime(); 161 | let etable = Chained(gas_etable, exec_etable); 162 | let precompiles = StandardPrecompileSet::new(&config); 163 | let resolver = EtableResolver::new(&config, &precompiles, &etable); 164 | let invoker = Invoker::new(&config, &resolver); 165 | let args = TransactArgs::Call { 166 | caller: test.transaction.sender, 167 | address: test.transaction.to, 168 | value: test.transaction.value, 169 | data: test.transaction.data, 170 | gas_limit: test.transaction.gas_limit, 171 | gas_price: test.transaction.gas_price, 172 | access_list: test 173 | .transaction 174 | .access_list 175 | .into_iter() 176 | .map(|access| (access.address, access.storage_keys)) 177 | .collect(), 178 | }; 179 | 180 | let initial_accessed = { 181 | let mut hots = BTreeSet::new(); 182 | for i in 1..10 { 183 | hots.insert((u256_to_h256(U256::from(i)).into(), None)); 184 | } 185 | hots 186 | }; 187 | 188 | let base_backend = InMemoryBackend { 189 | environment: env, 190 | state, 191 | }; 192 | 193 | let mut run_backend = OverlayedBackend::new(&base_backend, initial_accessed.clone(), &config); 194 | let mut step_backend = OverlayedBackend::new(&base_backend, initial_accessed.clone(), &config); 195 | 196 | // Run 197 | let run_result = evm::transact(args.clone(), Some(4), &mut run_backend, &invoker); 198 | let run_changeset = run_backend.deconstruct().1; 199 | let mut run_backend = base_backend.clone(); 200 | run_backend.apply_overlayed(&run_changeset); 201 | 202 | // Step 203 | if debug { 204 | let _step_result = evm::HeapTransact::new(args, &invoker, &mut step_backend).and_then( 205 | |mut stepper| loop { 206 | { 207 | if let Some(machine) = stepper.last_interpreter() { 208 | println!( 209 | "pc: {}, opcode: {:?}, gas: 0x{:x}", 210 | machine.position(), 211 | machine.peek_opcode(), 212 | machine.machine().state.gas(), 213 | ); 214 | } 215 | } 216 | if let Err(Capture::Exit(result)) = stepper.step() { 217 | break result; 218 | } 219 | }, 220 | ); 221 | let step_changeset = step_backend.deconstruct().1; 222 | let mut step_backend = base_backend.clone(); 223 | step_backend.apply_overlayed(&step_changeset); 224 | } 225 | 226 | let state_root = crate::hash::state_root(&run_backend); 227 | 228 | if test.post.expect_exception.is_some() { 229 | if run_result.is_err() { 230 | return Ok(()); 231 | } else { 232 | return Err(TestError::ExpectException.into()); 233 | } 234 | } 235 | 236 | if state_root != test.post.hash { 237 | if debug { 238 | for (address, account) in &run_backend.state { 239 | println!( 240 | "address: {:?}, balance: {}, nonce: {}, code: 0x{}, storage: {:?}", 241 | address, 242 | account.balance, 243 | account.nonce, 244 | hex::encode(&account.code), 245 | account.storage 246 | ); 247 | } 248 | } 249 | 250 | return Err(TestError::StateMismatch.into()); 251 | } 252 | 253 | Ok(()) 254 | } 255 | -------------------------------------------------------------------------------- /jsontests/src/types.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::BTreeMap, fmt}; 2 | 3 | use hex::FromHex; 4 | use primitive_types::{H160, H256, U256}; 5 | use serde::{ 6 | de::{Error, Visitor}, 7 | Deserialize, Deserializer, 8 | }; 9 | 10 | /// Statistic type to gather tests pass completion status 11 | #[derive(Default, Clone, Debug, Eq, PartialEq)] 12 | pub struct TestCompletionStatus { 13 | pub completed: usize, 14 | pub skipped: usize, 15 | } 16 | 17 | impl std::ops::AddAssign for TestCompletionStatus { 18 | fn add_assign(&mut self, rhs: Self) { 19 | self.completed += rhs.completed; 20 | self.skipped += rhs.skipped; 21 | } 22 | } 23 | 24 | impl TestCompletionStatus { 25 | /// Increment `completed` statistic field 26 | pub fn inc_completed(&mut self) { 27 | self.completed += 1 28 | } 29 | 30 | /// Increment `skipped` statistic field 31 | pub fn inc_skipped(&mut self) { 32 | self.skipped += 1 33 | } 34 | 35 | /// Get total passed tests 36 | pub fn get_total(&self) -> usize { 37 | self.completed + self.skipped 38 | } 39 | 40 | /// Print completion status. 41 | /// Most useful for single file completion statistic info 42 | pub fn print_completion(&self) { 43 | println!("COMPLETED: {} tests", self.completed); 44 | println!("SKIPPED: {} tests\n", self.skipped); 45 | } 46 | 47 | /// Print tests pass total statistic info for directory 48 | pub fn print_total_for_dir(&self, filename: &str) { 49 | println!( 50 | "TOTAL tests for: {filename}\n\tCOMPLETED: {}\n\tSKIPPED: {}", 51 | self.completed, self.skipped 52 | ); 53 | } 54 | 55 | // Print total statistics info 56 | pub fn print_total(&self) { 57 | println!( 58 | "\nTOTAL: {} tests\n\tCOMPLETED: {}\n\tSKIPPED: {}", 59 | self.get_total(), 60 | self.completed, 61 | self.skipped 62 | ); 63 | } 64 | } 65 | 66 | /// `TestMulti` represents raw data from `jsontest` data file. 67 | /// It contains multiple test data for passing tests. 68 | #[derive(Clone, Debug, Eq, PartialEq, Deserialize)] 69 | pub struct TestMulti { 70 | #[serde(rename = "_info")] 71 | pub info: TestInfo, 72 | pub env: TestEnv, 73 | pub post: BTreeMap>, 74 | pub pre: BTreeMap, 75 | pub transaction: TestMultiTransaction, 76 | } 77 | 78 | impl TestMulti { 79 | /// Fill tests data from `TestMulti` data. 80 | /// Return array of `TestData`, that represent single test, 81 | /// that ready to pass the test flow. 82 | pub fn tests(&self) -> Vec { 83 | let mut tests = Vec::new(); 84 | 85 | for (fork, post_states) in &self.post { 86 | for (index, post_state) in post_states.iter().enumerate() { 87 | tests.push(TestData { 88 | info: self.info.clone(), 89 | env: self.env.clone(), 90 | fork: *fork, 91 | index, 92 | post: post_state.clone(), 93 | pre: self.pre.clone(), 94 | transaction: TestTransaction { 95 | data: self.transaction.data[post_state.indexes.data].0.clone(), 96 | gas_limit: self.transaction.gas_limit[post_state.indexes.gas], 97 | gas_price: self 98 | .transaction 99 | .gas_price 100 | .unwrap_or(self.env.current_base_fee), 101 | gas_priority_fee: self.transaction.max_priority_fee_per_gas, 102 | nonce: self.transaction.nonce, 103 | secret_key: self.transaction.secret_key, 104 | sender: self.transaction.sender, 105 | to: self.transaction.to, 106 | value: self.transaction.value[post_state.indexes.value], 107 | access_list: match &self.transaction.access_lists { 108 | Some(access_lists) => access_lists[post_state.indexes.data].clone(), 109 | None => Vec::new(), 110 | }, 111 | }, 112 | }); 113 | } 114 | } 115 | 116 | tests 117 | } 118 | } 119 | 120 | /// Structure that contains data to run single test 121 | #[derive(Clone, Debug, Eq, PartialEq)] 122 | pub struct TestData { 123 | pub info: TestInfo, 124 | pub env: TestEnv, 125 | pub fork: Fork, 126 | pub index: usize, 127 | pub post: TestPostState, 128 | pub pre: BTreeMap, 129 | pub transaction: TestTransaction, 130 | } 131 | 132 | /// `TestInfo` contains information data about test from json file 133 | #[derive(Clone, Debug, Eq, PartialEq, Deserialize)] 134 | #[serde(rename_all = "camelCase")] 135 | pub struct TestInfo { 136 | pub comment: String, 137 | #[serde(rename = "filling-rpc-server")] 138 | pub filling_rpc_server: String, 139 | #[serde(rename = "filling-tool-version")] 140 | pub filling_tool_version: String, 141 | pub generated_test_hash: String, 142 | pub lllcversion: String, 143 | pub solidity: String, 144 | pub source: String, 145 | pub source_hash: String, 146 | } 147 | 148 | /// `TestEnv` represents Ethereum environment data 149 | #[derive(Clone, Debug, Eq, PartialEq, Deserialize)] 150 | #[serde(rename_all = "camelCase")] 151 | pub struct TestEnv { 152 | pub current_base_fee: U256, 153 | pub current_beacon_root: H256, 154 | pub current_coinbase: H160, 155 | pub current_difficulty: U256, 156 | pub current_gas_limit: U256, 157 | pub current_number: U256, 158 | pub current_random: H256, 159 | pub current_timestamp: U256, 160 | pub current_withdrawals_root: H256, 161 | pub previous_hash: H256, 162 | } 163 | 164 | /// Available Ethereum forks for testing 165 | #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Deserialize)] 166 | pub enum Fork { 167 | Berlin, 168 | Cancun, 169 | London, 170 | Merge, 171 | Shanghai, 172 | Byzantium, 173 | Constantinople, 174 | ConstantinopleFix, 175 | EIP150, 176 | EIP158, 177 | Frontier, 178 | Homestead, 179 | Istanbul, 180 | } 181 | 182 | #[derive(Clone, Debug, Eq, PartialEq, Deserialize)] 183 | #[serde(rename_all = "camelCase")] 184 | pub struct TestPostState { 185 | pub hash: H256, 186 | pub indexes: TestPostStateIndexes, 187 | pub logs: H256, 188 | pub txbytes: HexBytes, 189 | pub expect_exception: Option, 190 | } 191 | 192 | /// `TestExpectException` expected Ethereum exception 193 | #[derive(Clone, Debug, Eq, PartialEq, Deserialize)] 194 | #[allow(non_camel_case_types)] 195 | pub enum TestExpectException { 196 | TR_TypeNotSupported, 197 | TR_IntrinsicGas, 198 | } 199 | 200 | #[derive(Clone, Debug, Eq, PartialEq, Deserialize)] 201 | pub struct TestPostStateIndexes { 202 | pub data: usize, 203 | pub gas: usize, 204 | pub value: usize, 205 | } 206 | 207 | #[derive(Clone, Debug, Eq, PartialEq, Deserialize)] 208 | pub struct TestPreState { 209 | pub balance: U256, 210 | pub code: HexBytes, 211 | pub nonce: U256, 212 | pub storage: BTreeMap, 213 | } 214 | 215 | #[derive(Clone, Debug, Eq, PartialEq, Deserialize)] 216 | #[serde(rename_all = "camelCase")] 217 | pub struct TestMultiTransaction { 218 | pub data: Vec, 219 | pub gas_limit: Vec, 220 | pub gas_price: Option, 221 | pub max_fee_per_gas: Option, 222 | pub max_priority_fee_per_gas: Option, 223 | pub nonce: U256, 224 | pub secret_key: H256, 225 | pub sender: H160, 226 | pub to: H160, 227 | pub value: Vec, 228 | pub access_lists: Option>>, 229 | } 230 | 231 | #[derive(Clone, Debug, Eq, PartialEq, Deserialize)] 232 | #[serde(rename_all = "camelCase")] 233 | pub struct TestAccessListItem { 234 | pub address: H160, 235 | pub storage_keys: Vec, 236 | } 237 | 238 | #[derive(Clone, Debug, Eq, PartialEq)] 239 | pub struct TestTransaction { 240 | pub data: Vec, 241 | pub gas_limit: U256, 242 | pub gas_price: U256, 243 | pub gas_priority_fee: Option, 244 | pub nonce: U256, 245 | pub secret_key: H256, 246 | pub sender: H160, 247 | pub to: H160, 248 | pub value: U256, 249 | pub access_list: Vec, 250 | } 251 | 252 | #[derive(Clone, Debug, Eq, PartialEq, Deserialize)] 253 | pub struct HexBytes(#[serde(deserialize_with = "deserialize_hex_bytes")] pub Vec); 254 | 255 | fn deserialize_hex_bytes<'de, D>(deserializer: D) -> Result, D::Error> 256 | where 257 | D: Deserializer<'de>, 258 | { 259 | struct HexStrVisitor; 260 | 261 | impl<'de> Visitor<'de> for HexStrVisitor { 262 | type Value = Vec; 263 | 264 | fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { 265 | write!(f, "a hex encoded string") 266 | } 267 | 268 | fn visit_str(self, data: &str) -> Result 269 | where 270 | E: Error, 271 | { 272 | if &data[0..2] != "0x" { 273 | return Err(Error::custom("should start with 0x")); 274 | } 275 | 276 | FromHex::from_hex(&data[2..]).map_err(Error::custom) 277 | } 278 | 279 | fn visit_borrowed_str(self, data: &'de str) -> Result 280 | where 281 | E: Error, 282 | { 283 | if &data[0..2] != "0x" { 284 | return Err(Error::custom("should start with 0x")); 285 | } 286 | 287 | FromHex::from_hex(&data[2..]).map_err(Error::custom) 288 | } 289 | } 290 | 291 | deserializer.deserialize_str(HexStrVisitor) 292 | } 293 | -------------------------------------------------------------------------------- /precompile/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "evm-precompile" 3 | version = "0.0.0-dev" 4 | edition = { workspace = true } 5 | rust-version = "1.65.0" 6 | authors = { workspace = true } 7 | repository = { workspace = true } 8 | keywords = { workspace = true } 9 | description = "Standard EVM precompiles." 10 | 11 | [dependencies] 12 | bn = { package = "substrate-bn", version = "0.6", default-features = false } 13 | evm = { path = "..", default-features = false } 14 | k256 = { version = "0.13", features = ["ecdsa"], default-features = false } 15 | num = { version = "0.4", default-features = false, features = ["alloc"] } 16 | primitive-types = { version = "0.12", default-features = false, features = ["rlp"] } 17 | ripemd = { version = "0.1", default-features = false } 18 | sha2 = { version = "0.10", default-features = false } 19 | sha3 = { version = "0.10", default-features = false } 20 | 21 | [features] 22 | default = ["std"] 23 | std = [ 24 | "evm/std", 25 | "k256/std", 26 | "num/std", 27 | "primitive-types/std", 28 | "ripemd/std", 29 | "sha2/std", 30 | "sha3/std", 31 | ] 32 | -------------------------------------------------------------------------------- /precompile/src/blake2/eip152.rs: -------------------------------------------------------------------------------- 1 | /// The precomputed values for BLAKE2b [from the spec](https://tools.ietf.org/html/rfc7693#section-2.7) 2 | /// There are 10 16-byte arrays - one for each round 3 | /// the entries are calculated from the sigma constants. 4 | const SIGMA: [[usize; 16]; 10] = [ 5 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], 6 | [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3], 7 | [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4], 8 | [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8], 9 | [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13], 10 | [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9], 11 | [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11], 12 | [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10], 13 | [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5], 14 | [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0], 15 | ]; 16 | 17 | /// IV is the initialization vector for BLAKE2b. See https://tools.ietf.org/html/rfc7693#section-2.6 18 | /// for details. 19 | const IV: [u64; 8] = [ 20 | 0x6a09e667f3bcc908, 21 | 0xbb67ae8584caa73b, 22 | 0x3c6ef372fe94f82b, 23 | 0xa54ff53a5f1d36f1, 24 | 0x510e527fade682d1, 25 | 0x9b05688c2b3e6c1f, 26 | 0x1f83d9abfb41bd6b, 27 | 0x5be0cd19137e2179, 28 | ]; 29 | 30 | #[inline(always)] 31 | /// The G mixing function. See https://tools.ietf.org/html/rfc7693#section-3.1 32 | fn g(v: &mut [u64], a: usize, b: usize, c: usize, d: usize, x: u64, y: u64) { 33 | v[a] = v[a].wrapping_add(v[b]).wrapping_add(x); 34 | v[d] = (v[d] ^ v[a]).rotate_right(32); 35 | v[c] = v[c].wrapping_add(v[d]); 36 | v[b] = (v[b] ^ v[c]).rotate_right(24); 37 | v[a] = v[a].wrapping_add(v[b]).wrapping_add(y); 38 | v[d] = (v[d] ^ v[a]).rotate_right(16); 39 | v[c] = v[c].wrapping_add(v[d]); 40 | v[b] = (v[b] ^ v[c]).rotate_right(63); 41 | } 42 | 43 | /// The Blake2 compression function F. See https://tools.ietf.org/html/rfc7693#section-3.2 44 | /// Takes as an argument the state vector `h`, message block vector `m`, offset counter `t`, final 45 | /// block indicator flag `f`, and number of rounds `rounds`. The state vector provided as the first 46 | /// parameter is modified by the function. 47 | pub fn compress(h: &mut [u64; 8], m: [u64; 16], t: [u64; 2], f: bool, rounds: usize) { 48 | let mut v = [0u64; 16]; 49 | v[..h.len()].copy_from_slice(h); // First half from state. 50 | v[h.len()..].copy_from_slice(&IV); // Second half from IV. 51 | 52 | v[12] ^= t[0]; 53 | v[13] ^= t[1]; 54 | 55 | if f { 56 | v[14] = !v[14] // Invert all bits if the last-block-flag is set. 57 | } 58 | for i in 0..rounds { 59 | // Message word selection permutation for this round. 60 | let s = &SIGMA[i % 10]; 61 | g(&mut v, 0, 4, 8, 12, m[s[0]], m[s[1]]); 62 | g(&mut v, 1, 5, 9, 13, m[s[2]], m[s[3]]); 63 | g(&mut v, 2, 6, 10, 14, m[s[4]], m[s[5]]); 64 | g(&mut v, 3, 7, 11, 15, m[s[6]], m[s[7]]); 65 | 66 | g(&mut v, 0, 5, 10, 15, m[s[8]], m[s[9]]); 67 | g(&mut v, 1, 6, 11, 12, m[s[10]], m[s[11]]); 68 | g(&mut v, 2, 7, 8, 13, m[s[12]], m[s[13]]); 69 | g(&mut v, 3, 4, 9, 14, m[s[14]], m[s[15]]); 70 | } 71 | 72 | for i in 0..8 { 73 | h[i] ^= v[i] ^ v[i + 8]; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /precompile/src/blake2/mod.rs: -------------------------------------------------------------------------------- 1 | mod eip152; 2 | 3 | use evm::{ 4 | interpreter::error::{ExitException, ExitResult, ExitSucceed}, 5 | GasMutState, 6 | }; 7 | 8 | use crate::PurePrecompile; 9 | 10 | pub struct Blake2F; 11 | 12 | impl Blake2F { 13 | const GAS_COST_PER_ROUND: u64 = 1; // https://eips.ethereum.org/EIPS/eip-152#gas-costs-and-benchmarks 14 | } 15 | 16 | impl PurePrecompile for Blake2F { 17 | /// Format of `input`: 18 | /// [4 bytes for rounds][64 bytes for h][128 bytes for m][8 bytes for t_0][8 bytes for t_1][1 byte for f] 19 | fn execute(&self, input: &[u8], gasometer: &mut G) -> (ExitResult, Vec) { 20 | const BLAKE2_F_ARG_LEN: usize = 213; 21 | 22 | if input.len() != BLAKE2_F_ARG_LEN { 23 | return ( 24 | ExitException::Other( 25 | "input length for Blake2 F precompile should be exactly 213 bytes".into(), 26 | ) 27 | .into(), 28 | Vec::new(), 29 | ); 30 | } 31 | 32 | let mut rounds_buf: [u8; 4] = [0; 4]; 33 | rounds_buf.copy_from_slice(&input[0..4]); 34 | let rounds: u32 = u32::from_be_bytes(rounds_buf); 35 | 36 | let gas_cost: u64 = (rounds as u64) * Blake2F::GAS_COST_PER_ROUND; 37 | try_some!(gasometer.record_gas(gas_cost.into())); 38 | 39 | // we use from_le_bytes below to effectively swap byte order to LE if architecture is BE 40 | let mut h_buf: [u8; 64] = [0; 64]; 41 | h_buf.copy_from_slice(&input[4..68]); 42 | let mut h = [0u64; 8]; 43 | let mut ctr = 0; 44 | for state_word in &mut h { 45 | let mut temp: [u8; 8] = Default::default(); 46 | temp.copy_from_slice(&h_buf[(ctr * 8)..(ctr + 1) * 8]); 47 | *state_word = u64::from_le_bytes(temp); 48 | ctr += 1; 49 | } 50 | 51 | let mut m_buf: [u8; 128] = [0; 128]; 52 | m_buf.copy_from_slice(&input[68..196]); 53 | let mut m = [0u64; 16]; 54 | ctr = 0; 55 | for msg_word in &mut m { 56 | let mut temp: [u8; 8] = Default::default(); 57 | temp.copy_from_slice(&m_buf[(ctr * 8)..(ctr + 1) * 8]); 58 | *msg_word = u64::from_le_bytes(temp); 59 | ctr += 1; 60 | } 61 | 62 | let mut t_0_buf: [u8; 8] = [0; 8]; 63 | t_0_buf.copy_from_slice(&input[196..204]); 64 | let t_0 = u64::from_le_bytes(t_0_buf); 65 | 66 | let mut t_1_buf: [u8; 8] = [0; 8]; 67 | t_1_buf.copy_from_slice(&input[204..212]); 68 | let t_1 = u64::from_le_bytes(t_1_buf); 69 | 70 | let f = if input[212] == 1 { 71 | true 72 | } else if input[212] == 0 { 73 | false 74 | } else { 75 | return ( 76 | ExitException::Other("incorrect final block indicator flag".into()).into(), 77 | Vec::new(), 78 | ); 79 | }; 80 | 81 | eip152::compress(&mut h, m, [t_0, t_1], f, rounds as usize); 82 | 83 | let mut output_buf = [0u8; u64::BITS as usize]; 84 | for (i, state_word) in h.iter().enumerate() { 85 | output_buf[i * 8..(i + 1) * 8].copy_from_slice(&state_word.to_le_bytes()); 86 | } 87 | 88 | (ExitSucceed::Returned.into(), output_buf.to_vec()) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /precompile/src/bn128.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | 3 | use evm::{ 4 | interpreter::error::{ExitError, ExitException, ExitResult, ExitSucceed}, 5 | GasMutState, 6 | }; 7 | use primitive_types::U256; 8 | 9 | use crate::PurePrecompile; 10 | 11 | /// Copy bytes from input to target. 12 | fn read_input(source: &[u8], target: &mut [u8], offset: usize) { 13 | // Out of bounds, nothing to copy. 14 | if source.len() <= offset { 15 | return; 16 | } 17 | 18 | // Find len to copy up to target len, but not out of bounds. 19 | let len = core::cmp::min(target.len(), source.len() - offset); 20 | target[..len].copy_from_slice(&source[offset..][..len]); 21 | } 22 | 23 | fn read_fr(input: &[u8], start_inx: usize) -> Result { 24 | let mut buf = [0u8; 32]; 25 | read_input(input, &mut buf, start_inx); 26 | 27 | let ret = bn::Fr::from_slice(&buf) 28 | .map_err(|_| ExitException::Other("Invalid field element".into()))?; 29 | Ok(ret) 30 | } 31 | 32 | fn read_point(input: &[u8], start_inx: usize) -> Result { 33 | use bn::{AffineG1, Fq, Group, G1}; 34 | 35 | let mut px_buf = [0u8; 32]; 36 | let mut py_buf = [0u8; 32]; 37 | read_input(input, &mut px_buf, start_inx); 38 | read_input(input, &mut py_buf, start_inx + 32); 39 | 40 | let px = Fq::from_slice(&px_buf) 41 | .map_err(|_| ExitException::Other("Invalid point x coordinate".into()))?; 42 | 43 | let py = Fq::from_slice(&py_buf) 44 | .map_err(|_| ExitException::Other("Invalid point y coordinate".into()))?; 45 | 46 | Ok(if px == Fq::zero() && py == Fq::zero() { 47 | G1::zero() 48 | } else { 49 | AffineG1::new(px, py) 50 | .map_err(|_| ExitException::Other("Invalid curve point".into()))? 51 | .into() 52 | }) 53 | } 54 | 55 | /// The Bn128Add builtin 56 | pub struct Bn128Add; 57 | 58 | impl Bn128Add { 59 | const GAS_COST: u64 = 150; // https://eips.ethereum.org/EIPS/eip-1108 60 | } 61 | 62 | impl PurePrecompile for Bn128Add { 63 | fn execute(&self, input: &[u8], gasometer: &mut G) -> (ExitResult, Vec) { 64 | use bn::AffineG1; 65 | 66 | try_some!(gasometer.record_gas(Bn128Add::GAS_COST.into())); 67 | 68 | let p1 = try_some!(read_point(input, 0)); 69 | let p2 = try_some!(read_point(input, 64)); 70 | 71 | let mut buf = [0u8; 64]; 72 | if let Some(sum) = AffineG1::from_jacobian(p1 + p2) { 73 | // point not at infinity 74 | try_some!(sum 75 | .x() 76 | .to_big_endian(&mut buf[0..32]) 77 | .map_err(|_| ExitException::Other( 78 | "Cannot fail since 0..32 is 32-byte length".into() 79 | ))); 80 | try_some!(sum.y().to_big_endian(&mut buf[32..64]).map_err(|_| { 81 | ExitException::Other("Cannot fail since 32..64 is 32-byte length".into()) 82 | })); 83 | } 84 | 85 | (ExitSucceed::Returned.into(), buf.to_vec()) 86 | } 87 | } 88 | 89 | /// The Bn128Mul builtin 90 | pub struct Bn128Mul; 91 | 92 | impl Bn128Mul { 93 | const GAS_COST: u64 = 6_000; // https://eips.ethereum.org/EIPS/eip-1108 94 | } 95 | 96 | impl PurePrecompile for Bn128Mul { 97 | fn execute(&self, input: &[u8], gasometer: &mut G) -> (ExitResult, Vec) { 98 | use bn::AffineG1; 99 | 100 | try_some!(gasometer.record_gas(Bn128Mul::GAS_COST.into())); 101 | 102 | let p = try_some!(read_point(input, 0)); 103 | let fr = try_some!(read_fr(input, 64)); 104 | 105 | let mut buf = [0u8; 64]; 106 | if let Some(sum) = AffineG1::from_jacobian(p * fr) { 107 | // point not at infinity 108 | try_some!(sum 109 | .x() 110 | .to_big_endian(&mut buf[0..32]) 111 | .map_err(|_| ExitException::Other( 112 | "Cannot fail since 0..32 is 32-byte length".into() 113 | ))); 114 | try_some!(sum.y().to_big_endian(&mut buf[32..64]).map_err(|_| { 115 | ExitException::Other("Cannot fail since 32..64 is 32-byte length".into()) 116 | })); 117 | } 118 | 119 | (ExitSucceed::Returned.into(), buf.to_vec()) 120 | } 121 | } 122 | 123 | /// The Bn128Pairing builtin 124 | pub struct Bn128Pairing; 125 | 126 | impl Bn128Pairing { 127 | // https://eips.ethereum.org/EIPS/eip-1108 128 | const BASE_GAS_COST: u64 = 45_000; 129 | const GAS_COST_PER_PAIRING: u64 = 34_000; 130 | } 131 | 132 | impl PurePrecompile for Bn128Pairing { 133 | fn execute(&self, input: &[u8], gasometer: &mut G) -> (ExitResult, Vec) { 134 | use bn::{pairing_batch, AffineG1, AffineG2, Fq, Fq2, Group, Gt, G1, G2}; 135 | 136 | let ret_val = if input.is_empty() { 137 | try_some!(gasometer.record_gas(Bn128Pairing::BASE_GAS_COST.into())); 138 | U256::one() 139 | } else { 140 | if input.len() % 192 > 0 { 141 | return ( 142 | ExitException::Other("bad elliptic curve pairing size".into()).into(), 143 | Vec::new(), 144 | ); 145 | } 146 | 147 | // (a, b_a, b_b - each 64-byte affine coordinates) 148 | let elements = input.len() / 192; 149 | 150 | let gas_cost: u64 = Bn128Pairing::BASE_GAS_COST 151 | + (elements as u64 * Bn128Pairing::GAS_COST_PER_PAIRING); 152 | 153 | try_some!(gasometer.record_gas(gas_cost.into())); 154 | 155 | let mut vals = Vec::new(); 156 | for idx in 0..elements { 157 | let a_x = try_some!(Fq::from_slice(&input[idx * 192..idx * 192 + 32]) 158 | .map_err(|_| ExitException::Other("Invalid a argument x coordinate".into()))); 159 | 160 | let a_y = try_some!(Fq::from_slice(&input[idx * 192 + 32..idx * 192 + 64]) 161 | .map_err(|_| ExitException::Other("Invalid a argument y coordinate".into(),))); 162 | 163 | let b_a_y = try_some!(Fq::from_slice(&input[idx * 192 + 64..idx * 192 + 96]) 164 | .map_err(|_| { 165 | ExitException::Other( 166 | "Invalid b argument imaginary coeff x coordinate".into(), 167 | ) 168 | })); 169 | 170 | let b_a_x = try_some!(Fq::from_slice(&input[idx * 192 + 96..idx * 192 + 128]) 171 | .map_err(|_| ExitException::Other( 172 | "Invalid b argument imaginary coeff y coordinate".into(), 173 | ))); 174 | 175 | let b_b_y = try_some!(Fq::from_slice(&input[idx * 192 + 128..idx * 192 + 160]) 176 | .map_err(|_| { 177 | ExitException::Other("Invalid b argument real coeff x coordinate".into()) 178 | })); 179 | 180 | let b_b_x = try_some!(Fq::from_slice(&input[idx * 192 + 160..idx * 192 + 192]) 181 | .map_err(|_| { 182 | ExitException::Other("Invalid b argument real coeff y coordinate".into()) 183 | })); 184 | 185 | let b_a = Fq2::new(b_a_x, b_a_y); 186 | let b_b = Fq2::new(b_b_x, b_b_y); 187 | let b = if b_a.is_zero() && b_b.is_zero() { 188 | G2::zero() 189 | } else { 190 | G2::from(try_some!(AffineG2::new(b_a, b_b).map_err(|_| { 191 | ExitException::Other("Invalid b argument - not on curve".into()) 192 | }))) 193 | }; 194 | let a = if a_x.is_zero() && a_y.is_zero() { 195 | G1::zero() 196 | } else { 197 | G1::from(try_some!(AffineG1::new(a_x, a_y).map_err(|_| { 198 | ExitException::Other("Invalid a argument - not on curve".into()) 199 | },))) 200 | }; 201 | vals.push((a, b)); 202 | } 203 | 204 | let mul = pairing_batch(&vals); 205 | 206 | if mul == Gt::one() { 207 | U256::one() 208 | } else { 209 | U256::zero() 210 | } 211 | }; 212 | 213 | let mut buf = [0u8; 32]; 214 | ret_val.to_big_endian(&mut buf); 215 | 216 | (ExitSucceed::Returned.into(), buf.to_vec()) 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /precompile/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Standard EVM precompiles. 2 | 3 | // #![deny(warnings)] 4 | // #![forbid(unsafe_code, unused_variables)] 5 | #![cfg_attr(not(feature = "std"), no_std)] 6 | 7 | macro_rules! try_some { 8 | ($e:expr) => { 9 | match $e { 10 | Ok(v) => v, 11 | Err(err) => return (Err(err.into()), Vec::new()), 12 | } 13 | }; 14 | } 15 | 16 | extern crate alloc; 17 | 18 | mod blake2; 19 | mod bn128; 20 | mod modexp; 21 | mod simple; 22 | 23 | use alloc::vec::Vec; 24 | 25 | use evm::{ 26 | interpreter::{ 27 | error::{ExitError, ExitException, ExitResult}, 28 | runtime::RuntimeState, 29 | }, 30 | standard::{Config, PrecompileSet}, 31 | GasMutState, 32 | }; 33 | use primitive_types::H160; 34 | 35 | pub use crate::{ 36 | blake2::Blake2F, 37 | bn128::{Bn128Add, Bn128Mul, Bn128Pairing}, 38 | modexp::Modexp, 39 | simple::{ECRecover, Identity, Ripemd160, Sha256}, 40 | }; 41 | 42 | pub trait PurePrecompile { 43 | fn execute(&self, input: &[u8], gasometer: &mut G) -> (ExitResult, Vec); 44 | } 45 | 46 | pub struct StandardPrecompileSet<'config> { 47 | _config: &'config Config, 48 | } 49 | 50 | impl<'config> StandardPrecompileSet<'config> { 51 | pub fn new(config: &'config Config) -> Self { 52 | Self { _config: config } 53 | } 54 | } 55 | 56 | impl<'config, G: AsRef + GasMutState, H> PrecompileSet 57 | for StandardPrecompileSet<'config> 58 | { 59 | fn execute( 60 | &self, 61 | code_address: H160, 62 | input: &[u8], 63 | gasometer: &mut G, 64 | _handler: &mut H, 65 | ) -> Option<(ExitResult, Vec)> { 66 | // TODO: selectively disable precompiles based on config. 67 | 68 | if code_address == address(1) { 69 | Some(ECRecover.execute(input, gasometer)) 70 | } else if code_address == address(2) { 71 | Some(Sha256.execute(input, gasometer)) 72 | } else if code_address == address(3) { 73 | Some(Ripemd160.execute(input, gasometer)) 74 | } else if code_address == address(4) { 75 | Some(Identity.execute(input, gasometer)) 76 | } else if code_address == address(5) { 77 | Some(Modexp.execute(input, gasometer)) 78 | } else if code_address == address(6) { 79 | Some(Bn128Add.execute(input, gasometer)) 80 | } else if code_address == address(7) { 81 | Some(Bn128Mul.execute(input, gasometer)) 82 | } else if code_address == address(8) { 83 | Some(Bn128Pairing.execute(input, gasometer)) 84 | } else if code_address == address(9) { 85 | Some(Blake2F.execute(input, gasometer)) 86 | } else { 87 | None 88 | } 89 | } 90 | } 91 | 92 | fn linear_cost(len: u64, base: u64, word: u64) -> Result { 93 | let cost = base 94 | .checked_add( 95 | word.checked_mul(len.saturating_add(31) / 32) 96 | .ok_or(ExitException::OutOfGas)?, 97 | ) 98 | .ok_or(ExitException::OutOfGas)?; 99 | 100 | Ok(cost) 101 | } 102 | 103 | const fn address(last: u8) -> H160 { 104 | H160([ 105 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, last, 106 | ]) 107 | } 108 | -------------------------------------------------------------------------------- /precompile/src/modexp.rs: -------------------------------------------------------------------------------- 1 | use alloc::{vec, vec::Vec}; 2 | use core::cmp::max; 3 | 4 | use evm::{ 5 | interpreter::error::{ExitException, ExitResult, ExitSucceed}, 6 | GasMutState, 7 | }; 8 | use num::{BigUint, FromPrimitive, Integer, One, ToPrimitive, Zero}; 9 | 10 | use crate::PurePrecompile; 11 | 12 | pub struct Modexp; 13 | 14 | const MIN_GAS_COST: u64 = 200; 15 | 16 | // Calculate gas cost according to EIP 2565: 17 | // https://eips.ethereum.org/EIPS/eip-2565 18 | fn calculate_gas_cost( 19 | base_length: u64, 20 | mod_length: u64, 21 | exponent: &BigUint, 22 | exponent_bytes: &[u8], 23 | mod_is_even: bool, 24 | ) -> u64 { 25 | fn calculate_multiplication_complexity(base_length: u64, mod_length: u64) -> u64 { 26 | let max_length = max(base_length, mod_length); 27 | let mut words = max_length / 8; 28 | if max_length % 8 > 0 { 29 | words += 1; 30 | } 31 | 32 | // Note: can't overflow because we take words to be some u64 value / 8, which is 33 | // necessarily less than sqrt(u64::MAX). 34 | // Additionally, both base_length and mod_length are bounded to 1024, so this has 35 | // an upper bound of roughly (1024 / 8) squared 36 | words * words 37 | } 38 | 39 | fn calculate_iteration_count(exponent: &BigUint, exponent_bytes: &[u8]) -> u64 { 40 | let mut iteration_count: u64 = 0; 41 | let exp_length = exponent_bytes.len() as u64; 42 | 43 | if exp_length <= 32 && exponent.is_zero() { 44 | iteration_count = 0; 45 | } else if exp_length <= 32 { 46 | iteration_count = exponent.bits() - 1; 47 | } else if exp_length > 32 { 48 | // from the EIP spec: 49 | // (8 * (exp_length - 32)) + ((exponent & (2**256 - 1)).bit_length() - 1) 50 | // 51 | // Notes: 52 | // * exp_length is bounded to 1024 and is > 32 53 | // * exponent can be zero, so we subtract 1 after adding the other terms (whose sum 54 | // must be > 0) 55 | // * the addition can't overflow because the terms are both capped at roughly 56 | // 8 * max size of exp_length (1024) 57 | // * the EIP spec is written in python, in which (exponent & (2**256 - 1)) takes the 58 | // FIRST 32 bytes. However this `BigUint` `&` operator takes the LAST 32 bytes. 59 | // We thus instead take the bytes manually. 60 | let exponent_head = BigUint::from_bytes_be(&exponent_bytes[..32]); 61 | 62 | iteration_count = (8 * (exp_length - 32)) + exponent_head.bits() - 1; 63 | } 64 | 65 | max(iteration_count, 1) 66 | } 67 | 68 | let multiplication_complexity = calculate_multiplication_complexity(base_length, mod_length); 69 | let iteration_count = calculate_iteration_count(exponent, exponent_bytes); 70 | max( 71 | MIN_GAS_COST, 72 | multiplication_complexity * iteration_count / 3, 73 | ) 74 | .saturating_mul(if mod_is_even { 20 } else { 1 }) 75 | } 76 | 77 | /// Copy bytes from input to target. 78 | fn read_input(source: &[u8], target: &mut [u8], source_offset: &mut usize) { 79 | // We move the offset by the len of the target, regardless of what we 80 | // actually copy. 81 | let offset = *source_offset; 82 | *source_offset += target.len(); 83 | 84 | // Out of bounds, nothing to copy. 85 | if source.len() <= offset { 86 | return; 87 | } 88 | 89 | // Find len to copy up to target len, but not out of bounds. 90 | let len = core::cmp::min(target.len(), source.len() - offset); 91 | target[..len].copy_from_slice(&source[offset..][..len]); 92 | } 93 | 94 | impl PurePrecompile for Modexp { 95 | fn execute(&self, input: &[u8], gasometer: &mut G) -> (ExitResult, Vec) { 96 | let mut input_offset = 0; 97 | 98 | // Yellowpaper: whenever the input is too short, the missing bytes are 99 | // considered to be zero. 100 | let mut base_len_buf = [0u8; 32]; 101 | read_input(input, &mut base_len_buf, &mut input_offset); 102 | let mut exp_len_buf = [0u8; 32]; 103 | read_input(input, &mut exp_len_buf, &mut input_offset); 104 | let mut mod_len_buf = [0u8; 32]; 105 | read_input(input, &mut mod_len_buf, &mut input_offset); 106 | 107 | // reasonable assumption: this must fit within the Ethereum EVM's max stack size 108 | let max_size_big = BigUint::from_u32(1024).expect("can't create BigUint"); 109 | 110 | let base_len_big = BigUint::from_bytes_be(&base_len_buf); 111 | if base_len_big > max_size_big { 112 | try_some!(Err(ExitException::Other( 113 | "unreasonably large base length".into() 114 | ))); 115 | } 116 | 117 | let exp_len_big = BigUint::from_bytes_be(&exp_len_buf); 118 | if exp_len_big > max_size_big { 119 | try_some!(Err(ExitException::Other( 120 | "unreasonably large exponent length".into() 121 | ))); 122 | } 123 | 124 | let mod_len_big = BigUint::from_bytes_be(&mod_len_buf); 125 | if mod_len_big > max_size_big { 126 | try_some!(Err(ExitException::Other( 127 | "unreasonably large modulus length".into() 128 | ))); 129 | } 130 | 131 | // bounds check handled above 132 | let base_len = base_len_big.to_usize().expect("base_len out of bounds"); 133 | let exp_len = exp_len_big.to_usize().expect("exp_len out of bounds"); 134 | let mod_len = mod_len_big.to_usize().expect("mod_len out of bounds"); 135 | 136 | // if mod_len is 0 output must be empty 137 | if mod_len == 0 { 138 | return (ExitSucceed::Returned.into(), Vec::new()); 139 | } 140 | 141 | // Gas formula allows arbitrary large exp_len when base and modulus are empty, so we need to handle empty base first. 142 | let r = if base_len == 0 && mod_len == 0 { 143 | try_some!(gasometer.record_gas(MIN_GAS_COST.into())); 144 | BigUint::zero() 145 | } else { 146 | // read the numbers themselves. 147 | let mut base_buf = vec![0u8; base_len]; 148 | read_input(input, &mut base_buf, &mut input_offset); 149 | let base = BigUint::from_bytes_be(&base_buf); 150 | 151 | let mut exp_buf = vec![0u8; exp_len]; 152 | read_input(input, &mut exp_buf, &mut input_offset); 153 | let exponent = BigUint::from_bytes_be(&exp_buf); 154 | 155 | let mut mod_buf = vec![0u8; mod_len]; 156 | read_input(input, &mut mod_buf, &mut input_offset); 157 | let modulus = BigUint::from_bytes_be(&mod_buf); 158 | 159 | // do our gas accounting 160 | let gas_cost = calculate_gas_cost( 161 | base_len as u64, 162 | mod_len as u64, 163 | &exponent, 164 | &exp_buf, 165 | modulus.is_even(), 166 | ); 167 | 168 | try_some!(gasometer.record_gas(gas_cost.into())); 169 | 170 | if modulus.is_zero() || modulus.is_one() { 171 | BigUint::zero() 172 | } else { 173 | base.modpow(&exponent, &modulus) 174 | } 175 | }; 176 | 177 | // write output to given memory, left padded and same length as the modulus. 178 | let bytes = r.to_bytes_be(); 179 | 180 | // always true except in the case of zero-length modulus, which leads to 181 | // output of length and value 1. 182 | #[allow(clippy::comparison_chain)] 183 | if bytes.len() == mod_len { 184 | (ExitSucceed::Returned.into(), bytes.to_vec()) 185 | } else if bytes.len() < mod_len { 186 | let mut ret = Vec::with_capacity(mod_len); 187 | ret.extend(core::iter::repeat(0).take(mod_len - bytes.len())); 188 | ret.extend_from_slice(&bytes[..]); 189 | (ExitSucceed::Returned.into(), ret.to_vec()) 190 | } else { 191 | return (ExitException::Other("failed".into()).into(), Vec::new()); 192 | } 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /precompile/src/simple.rs: -------------------------------------------------------------------------------- 1 | use core::cmp::min; 2 | 3 | use evm::{ 4 | interpreter::error::{ExitException, ExitResult, ExitSucceed}, 5 | GasMutState, 6 | }; 7 | use k256::ecdsa::{RecoveryId, Signature, VerifyingKey}; 8 | use primitive_types::{H256, U256}; 9 | use sha3::{Digest, Keccak256}; 10 | 11 | use crate::{linear_cost, PurePrecompile}; 12 | 13 | pub struct ECRecover; 14 | 15 | impl PurePrecompile for ECRecover { 16 | fn execute(&self, i: &[u8], gasometer: &mut G) -> (ExitResult, Vec) { 17 | const COST_BASE: u64 = 3000; 18 | const COST_WORD: u64 = 0; 19 | try_some!(gasometer.record_gas(U256::from(try_some!(linear_cost( 20 | i.len() as u64, 21 | COST_BASE, 22 | COST_WORD 23 | ))))); 24 | 25 | let mut input = [0u8; 128]; 26 | input[..min(i.len(), 128)].copy_from_slice(&i[..min(i.len(), 128)]); 27 | 28 | // v can only be 27 or 28 on the full 32 bytes value. 29 | // https://github.com/ethereum/go-ethereum/blob/a907d7e81aaeea15d80b2d3209ad8e08e3bf49e0/core/vm/contracts.go#L177 30 | if input[32..63] != [0u8; 31] || ![27, 28].contains(&input[63]) { 31 | return (ExitSucceed::Returned.into(), Vec::new()); 32 | } 33 | 34 | let mut msg = [0u8; 32]; 35 | let mut sig = [0u8; 64]; 36 | 37 | msg[0..32].copy_from_slice(&input[0..32]); 38 | sig[0..32].copy_from_slice(&input[64..96]); // r 39 | sig[32..64].copy_from_slice(&input[96..128]); // s 40 | let sig = try_some!(Signature::from_bytes((&sig[..]).into()) 41 | .map_err(|_| ExitException::Other("invalid ecdsa sig".into()))); 42 | let recid = try_some!(RecoveryId::from_byte(input[63] - 27) 43 | .ok_or(ExitException::Other("invalid recoverty id".into()))); // v 44 | 45 | let pubkey = try_some!(VerifyingKey::recover_from_prehash(&msg[..], &sig, recid) 46 | .map_err(|_| ExitException::Other("recover key failed".into()))); 47 | let mut address = 48 | H256::from_slice(Keccak256::digest(&pubkey.to_sec1_bytes()[..]).as_slice()); 49 | address.0[0..12].copy_from_slice(&[0u8; 12]); 50 | 51 | (ExitSucceed::Returned.into(), address.0.to_vec()) 52 | } 53 | } 54 | 55 | pub struct Sha256; 56 | 57 | impl PurePrecompile for Sha256 { 58 | fn execute(&self, input: &[u8], gasometer: &mut G) -> (ExitResult, Vec) { 59 | const COST_BASE: u64 = 600; 60 | const COST_WORD: u64 = 120; 61 | try_some!(gasometer.record_gas(U256::from(try_some!(linear_cost( 62 | input.len() as u64, 63 | COST_BASE, 64 | COST_WORD 65 | ))))); 66 | 67 | let mut ret = [0u8; 32]; 68 | let hash = ripemd::Ripemd160::digest(input); 69 | ret[12..32].copy_from_slice(&hash); 70 | 71 | (ExitSucceed::Returned.into(), ret.to_vec()) 72 | } 73 | } 74 | 75 | pub struct Ripemd160; 76 | 77 | impl PurePrecompile for Ripemd160 { 78 | fn execute(&self, input: &[u8], gasometer: &mut G) -> (ExitResult, Vec) { 79 | const COST_BASE: u64 = 60; 80 | const COST_WORD: u64 = 12; 81 | try_some!(gasometer.record_gas(U256::from(try_some!(linear_cost( 82 | input.len() as u64, 83 | COST_BASE, 84 | COST_WORD 85 | ))))); 86 | 87 | let hash = sha2::Sha256::digest(input); 88 | 89 | (ExitSucceed::Returned.into(), hash.to_vec()) 90 | } 91 | } 92 | 93 | pub struct Identity; 94 | 95 | impl PurePrecompile for Identity { 96 | fn execute(&self, input: &[u8], gasometer: &mut G) -> (ExitResult, Vec) { 97 | const COST_BASE: u64 = 15; 98 | const COST_WORD: u64 = 3; 99 | try_some!(gasometer.record_gas(U256::from(try_some!(linear_cost( 100 | input.len() as u64, 101 | COST_BASE, 102 | COST_WORD 103 | ))))); 104 | 105 | (ExitSucceed::Returned.into(), input.to_vec()) 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "1.87.0" 3 | profile = "minimal" 4 | components = [ "rustfmt", "clippy" ] 5 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | hard_tabs = true 2 | imports_granularity="Crate" 3 | group_imports = "StdExternalCrate" 4 | -------------------------------------------------------------------------------- /src/backend/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Backend-related traits and implementations 2 | //! 3 | //! A backend exposes external information that is available to an EVM 4 | //! interpreter. This includes block information such as the current coinbase, 5 | //! block gas limit, etc, as well as the state such as account balance, storage 6 | //! and code. 7 | //! 8 | //! Backends have layers, representing information that may be committed or 9 | //! discard after the current call stack finishes. Due to the vast differences of 10 | //! how different backends behave (for example, in some backends like wasm, 11 | //! pushing/poping layers are dealt by extern functions), layers are handled 12 | //! internally inside a backend. 13 | 14 | mod overlayed; 15 | 16 | pub use evm_interpreter::runtime::{RuntimeBackend, RuntimeBaseBackend, RuntimeEnvironment}; 17 | 18 | pub use self::overlayed::{OverlayedBackend, OverlayedChangeSet}; 19 | 20 | /// Backend with layers that can transactionally be committed or discarded. 21 | pub trait TransactionalBackend { 22 | /// Push a new substate layer into the backend. 23 | fn push_substate(&mut self); 24 | /// Pop the last substate layer from the backend, either committing or 25 | /// discarding it. 26 | /// 27 | /// The caller is expected to maintain balance of push/pop, and the backend 28 | /// are free to panic if it does not. 29 | fn pop_substate(&mut self, strategy: crate::MergeStrategy); 30 | } 31 | -------------------------------------------------------------------------------- /src/gasometer.rs: -------------------------------------------------------------------------------- 1 | //! EVM gasometer. 2 | 3 | use evm_interpreter::{error::ExitError, runtime::GasState}; 4 | use primitive_types::U256; 5 | 6 | pub trait GasMutState: GasState { 7 | fn record_gas(&mut self, gas: U256) -> Result<(), ExitError>; 8 | } 9 | -------------------------------------------------------------------------------- /src/invoker.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | 3 | use evm_interpreter::{ 4 | error::{Capture, ExitError, ExitResult}, 5 | Interpreter, 6 | }; 7 | 8 | /// Control for an invoker. 9 | pub enum InvokerControl { 10 | /// Pushing the call stack. 11 | Enter(VE), 12 | /// Directly exit, not pushing the call stack. 13 | DirectExit(VD), 14 | } 15 | 16 | /// An invoker, responsible for pushing/poping values in the call stack. 17 | pub trait Invoker { 18 | type State; 19 | type Interpreter: Interpreter; 20 | /// Possible interrupt type that may be returned by the call stack. 21 | type Interrupt; 22 | 23 | /// Type for transaction arguments. 24 | type TransactArgs; 25 | /// The invoke of a top-layer transaction call stack. When finalizing a 26 | /// transaction, this invoke is used to figure out the finalization routine. 27 | type TransactInvoke; 28 | /// The returned value of the transaction. 29 | type TransactValue; 30 | /// The invoke of a sub-layer call stack. When exiting a call stack, this 31 | /// invoke is used to figure out the exit routine. 32 | type SubstackInvoke; 33 | 34 | /// Create a new transaction with the given transaction arguments. 35 | #[allow(clippy::type_complexity)] 36 | fn new_transact( 37 | &self, 38 | args: Self::TransactArgs, 39 | handler: &mut H, 40 | ) -> Result< 41 | ( 42 | Self::TransactInvoke, 43 | InvokerControl))>, 44 | ), 45 | ExitError, 46 | >; 47 | 48 | /// Finalize a transaction. 49 | fn finalize_transact( 50 | &self, 51 | invoke: &Self::TransactInvoke, 52 | exit: ExitResult, 53 | machine: (Self::State, Vec), 54 | handler: &mut H, 55 | ) -> Result; 56 | 57 | /// Enter a sub-layer call stack. 58 | #[allow(clippy::type_complexity)] 59 | fn enter_substack( 60 | &self, 61 | trap: Tr, 62 | machine: &mut Self::Interpreter, 63 | handler: &mut H, 64 | depth: usize, 65 | ) -> Capture< 66 | Result< 67 | ( 68 | Self::SubstackInvoke, 69 | InvokerControl))>, 70 | ), 71 | ExitError, 72 | >, 73 | Self::Interrupt, 74 | >; 75 | 76 | /// Exit a sub-layer call stack. 77 | fn exit_substack( 78 | &self, 79 | result: ExitResult, 80 | child: (Self::State, Vec), 81 | trap_data: Self::SubstackInvoke, 82 | parent: &mut Self::Interpreter, 83 | handler: &mut H, 84 | ) -> Result<(), ExitError>; 85 | } 86 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Ethereum Virtual Machine in Rust 2 | //! 3 | //! Rust EVM is a flexible Ethereum Virtual Machine interpreter that can be 4 | //! easily customized. 5 | //! 6 | //! ## Basic usage 7 | //! 8 | //! The entrypoint of a normal EVM execution is through the [transact] function. 9 | //! The [transact] function implements a hybrid (stack-based, and then 10 | //! heap-based) call stack. 11 | //! 12 | //! To use the [transact] function, you will need to first implement a 13 | //! backend. This is anything that implements [RuntimeEnvironment], 14 | //! [RuntimeBaseBackend] and [RuntimeBackend] traits. You will also need to 15 | //! select a few other components to construct the `invoker` parameter needed 16 | //! for the function. 17 | //! 18 | //! * Select an [Invoker]. The invoker defines all details of the execution 19 | //! environment except the external backend. [standard::Invoker] is 20 | //! probably want you want if you are not extending EVM. 21 | //! * For the standard invoker, select a [standard::Config], which represents 22 | //! different Ethereum hard forks. 23 | //! * Select the precompile set. You may want the `StandardPrecompileSet` in 24 | //! `evm-precompile` crate. 25 | //! * Select a resolver. This defines how the interpreter machines are resolved 26 | //! given a code address for call or an init code for create. You may want 27 | //! [standard::EtableResolver], which accepts a precompile set. 28 | //! 29 | //! ## Debugging 30 | //! 31 | //! Rust EVM supports two different methods for debugging. You can either single 32 | //! step the execution, or you can trace the opcodes. 33 | //! 34 | //! ### Single stepping 35 | //! 36 | //! Single stepping allows you to examine the full machine internal state every 37 | //! time the interpreter finishes executing a single opcode. To do this, use the 38 | //! heap-only call stack [HeapTransact]. Parameters passed to [HeapTransact] are 39 | //! the same as [transact]. 40 | //! 41 | //! ### Tracing 42 | //! 43 | //! The interpreter machine uses information from an [Etable] to decide how each 44 | //! opcode behaves. An [Etable] is fully customizable and a helper function is 45 | //! also provided [Etable::wrap]. 46 | //! 47 | //! If you also want to trace inside gasometers, simply create a wrapper struct 48 | //! of the gasometer you use, and pass that into the invoker. 49 | //! 50 | //! ## Customization 51 | //! 52 | //! All aspects of the interpreter can be customized individually. 53 | //! 54 | //! * New opcodes can be added or customized through [Etable]. 55 | //! * Gas metering behavior can be customized by wrapping [standard::Gasometer] or creating new 56 | //! ones. 57 | //! * Code resolution and precompiles can be customized by [standard::Resolver]. 58 | //! * Call invocation and transaction behavior can be customized via [standard::Invoker]. 59 | //! * Finally, each machine on the call stack has the concept of [Color], which allows you to 60 | //! implement account versioning, or specialized precompiles that invoke subcalls. 61 | 62 | #![deny(warnings)] 63 | #![forbid(unsafe_code, unused_variables)] 64 | #![cfg_attr(not(feature = "std"), no_std)] 65 | 66 | extern crate alloc; 67 | 68 | pub mod backend; 69 | pub mod standard; 70 | 71 | mod call_stack; 72 | mod gasometer; 73 | mod invoker; 74 | 75 | pub use evm_interpreter as interpreter; 76 | 77 | pub use crate::{ 78 | backend::TransactionalBackend, 79 | call_stack::{transact, HeapTransact}, 80 | gasometer::GasMutState, 81 | invoker::{Invoker, InvokerControl}, 82 | }; 83 | 84 | /// Merge strategy of a backend substate layer or a call stack gasometer layer. 85 | #[derive(Clone, Debug, Copy)] 86 | pub enum MergeStrategy { 87 | /// Fully commit the sub-layer into the parent. This happens if the sub-machine executes 88 | /// successfully. 89 | Commit, 90 | /// Revert the state, but keep remaining gases. This happens with the `REVERT` opcode. 91 | Revert, 92 | /// Discard the state and gases. This happens in all situations where the machine encounters an 93 | /// error. 94 | Discard, 95 | } 96 | -------------------------------------------------------------------------------- /src/standard/gasometer/consts.rs: -------------------------------------------------------------------------------- 1 | use evm_interpreter::opcode::Opcode; 2 | 3 | pub const G_ZERO: u64 = 0; 4 | pub const G_BASE: u64 = 2; 5 | pub const G_VERYLOW: u64 = 3; 6 | pub const G_LOW: u64 = 5; 7 | pub const G_MID: u64 = 8; 8 | pub const G_HIGH: u64 = 10; 9 | pub const G_JUMPDEST: u64 = 1; 10 | pub const R_SUICIDE: i64 = 24000; 11 | pub const G_CREATE: u64 = 32000; 12 | pub const G_CALLVALUE: u64 = 9000; 13 | pub const G_NEWACCOUNT: u64 = 25000; 14 | pub const G_EXP: u64 = 10; 15 | pub const G_MEMORY: u64 = 3; 16 | pub const G_LOG: u64 = 375; 17 | pub const G_LOGDATA: u64 = 8; 18 | pub const G_LOGTOPIC: u64 = 375; 19 | pub const G_SHA3: u64 = 30; 20 | pub const G_SHA3WORD: u64 = 6; 21 | pub const G_COPY: u64 = 3; 22 | pub const G_BLOCKHASH: u64 = 20; 23 | pub const G_CODEDEPOSIT: u64 = 200; 24 | 25 | pub static STATIC_COST_TABLE: [Option; 256] = { 26 | let mut table = [None; 256]; 27 | 28 | table[Opcode::STOP.as_usize()] = Some(G_ZERO); 29 | table[Opcode::CALLDATASIZE.as_usize()] = Some(G_BASE); 30 | table[Opcode::CODESIZE.as_usize()] = Some(G_BASE); 31 | table[Opcode::POP.as_usize()] = Some(G_BASE); 32 | table[Opcode::PC.as_usize()] = Some(G_BASE); 33 | table[Opcode::MSIZE.as_usize()] = Some(G_BASE); 34 | 35 | table[Opcode::ADDRESS.as_usize()] = Some(G_BASE); 36 | table[Opcode::ORIGIN.as_usize()] = Some(G_BASE); 37 | table[Opcode::CALLER.as_usize()] = Some(G_BASE); 38 | table[Opcode::CALLVALUE.as_usize()] = Some(G_BASE); 39 | table[Opcode::COINBASE.as_usize()] = Some(G_BASE); 40 | table[Opcode::TIMESTAMP.as_usize()] = Some(G_BASE); 41 | table[Opcode::NUMBER.as_usize()] = Some(G_BASE); 42 | table[Opcode::DIFFICULTY.as_usize()] = Some(G_BASE); 43 | table[Opcode::GASLIMIT.as_usize()] = Some(G_BASE); 44 | table[Opcode::GASPRICE.as_usize()] = Some(G_BASE); 45 | table[Opcode::GAS.as_usize()] = Some(G_BASE); 46 | 47 | table[Opcode::ADD.as_usize()] = Some(G_VERYLOW); 48 | table[Opcode::SUB.as_usize()] = Some(G_VERYLOW); 49 | table[Opcode::NOT.as_usize()] = Some(G_VERYLOW); 50 | table[Opcode::LT.as_usize()] = Some(G_VERYLOW); 51 | table[Opcode::GT.as_usize()] = Some(G_VERYLOW); 52 | table[Opcode::SLT.as_usize()] = Some(G_VERYLOW); 53 | table[Opcode::SGT.as_usize()] = Some(G_VERYLOW); 54 | table[Opcode::EQ.as_usize()] = Some(G_VERYLOW); 55 | table[Opcode::ISZERO.as_usize()] = Some(G_VERYLOW); 56 | table[Opcode::AND.as_usize()] = Some(G_VERYLOW); 57 | table[Opcode::OR.as_usize()] = Some(G_VERYLOW); 58 | table[Opcode::XOR.as_usize()] = Some(G_VERYLOW); 59 | table[Opcode::BYTE.as_usize()] = Some(G_VERYLOW); 60 | table[Opcode::CALLDATALOAD.as_usize()] = Some(G_VERYLOW); 61 | table[Opcode::PUSH1.as_usize()] = Some(G_VERYLOW); 62 | table[Opcode::PUSH2.as_usize()] = Some(G_VERYLOW); 63 | table[Opcode::PUSH3.as_usize()] = Some(G_VERYLOW); 64 | table[Opcode::PUSH4.as_usize()] = Some(G_VERYLOW); 65 | table[Opcode::PUSH5.as_usize()] = Some(G_VERYLOW); 66 | table[Opcode::PUSH6.as_usize()] = Some(G_VERYLOW); 67 | table[Opcode::PUSH7.as_usize()] = Some(G_VERYLOW); 68 | table[Opcode::PUSH8.as_usize()] = Some(G_VERYLOW); 69 | table[Opcode::PUSH9.as_usize()] = Some(G_VERYLOW); 70 | table[Opcode::PUSH10.as_usize()] = Some(G_VERYLOW); 71 | table[Opcode::PUSH11.as_usize()] = Some(G_VERYLOW); 72 | table[Opcode::PUSH12.as_usize()] = Some(G_VERYLOW); 73 | table[Opcode::PUSH13.as_usize()] = Some(G_VERYLOW); 74 | table[Opcode::PUSH14.as_usize()] = Some(G_VERYLOW); 75 | table[Opcode::PUSH15.as_usize()] = Some(G_VERYLOW); 76 | table[Opcode::PUSH16.as_usize()] = Some(G_VERYLOW); 77 | table[Opcode::PUSH17.as_usize()] = Some(G_VERYLOW); 78 | table[Opcode::PUSH18.as_usize()] = Some(G_VERYLOW); 79 | table[Opcode::PUSH19.as_usize()] = Some(G_VERYLOW); 80 | table[Opcode::PUSH20.as_usize()] = Some(G_VERYLOW); 81 | table[Opcode::PUSH21.as_usize()] = Some(G_VERYLOW); 82 | table[Opcode::PUSH22.as_usize()] = Some(G_VERYLOW); 83 | table[Opcode::PUSH23.as_usize()] = Some(G_VERYLOW); 84 | table[Opcode::PUSH24.as_usize()] = Some(G_VERYLOW); 85 | table[Opcode::PUSH25.as_usize()] = Some(G_VERYLOW); 86 | table[Opcode::PUSH26.as_usize()] = Some(G_VERYLOW); 87 | table[Opcode::PUSH27.as_usize()] = Some(G_VERYLOW); 88 | table[Opcode::PUSH28.as_usize()] = Some(G_VERYLOW); 89 | table[Opcode::PUSH29.as_usize()] = Some(G_VERYLOW); 90 | table[Opcode::PUSH30.as_usize()] = Some(G_VERYLOW); 91 | table[Opcode::PUSH31.as_usize()] = Some(G_VERYLOW); 92 | table[Opcode::PUSH32.as_usize()] = Some(G_VERYLOW); 93 | table[Opcode::DUP1.as_usize()] = Some(G_VERYLOW); 94 | table[Opcode::DUP2.as_usize()] = Some(G_VERYLOW); 95 | table[Opcode::DUP3.as_usize()] = Some(G_VERYLOW); 96 | table[Opcode::DUP4.as_usize()] = Some(G_VERYLOW); 97 | table[Opcode::DUP5.as_usize()] = Some(G_VERYLOW); 98 | table[Opcode::DUP6.as_usize()] = Some(G_VERYLOW); 99 | table[Opcode::DUP7.as_usize()] = Some(G_VERYLOW); 100 | table[Opcode::DUP8.as_usize()] = Some(G_VERYLOW); 101 | table[Opcode::DUP9.as_usize()] = Some(G_VERYLOW); 102 | table[Opcode::DUP10.as_usize()] = Some(G_VERYLOW); 103 | table[Opcode::DUP11.as_usize()] = Some(G_VERYLOW); 104 | table[Opcode::DUP12.as_usize()] = Some(G_VERYLOW); 105 | table[Opcode::DUP13.as_usize()] = Some(G_VERYLOW); 106 | table[Opcode::DUP14.as_usize()] = Some(G_VERYLOW); 107 | table[Opcode::DUP15.as_usize()] = Some(G_VERYLOW); 108 | table[Opcode::DUP16.as_usize()] = Some(G_VERYLOW); 109 | table[Opcode::SWAP1.as_usize()] = Some(G_VERYLOW); 110 | table[Opcode::SWAP2.as_usize()] = Some(G_VERYLOW); 111 | table[Opcode::SWAP3.as_usize()] = Some(G_VERYLOW); 112 | table[Opcode::SWAP4.as_usize()] = Some(G_VERYLOW); 113 | table[Opcode::SWAP5.as_usize()] = Some(G_VERYLOW); 114 | table[Opcode::SWAP6.as_usize()] = Some(G_VERYLOW); 115 | table[Opcode::SWAP7.as_usize()] = Some(G_VERYLOW); 116 | table[Opcode::SWAP8.as_usize()] = Some(G_VERYLOW); 117 | table[Opcode::SWAP9.as_usize()] = Some(G_VERYLOW); 118 | table[Opcode::SWAP10.as_usize()] = Some(G_VERYLOW); 119 | table[Opcode::SWAP11.as_usize()] = Some(G_VERYLOW); 120 | table[Opcode::SWAP12.as_usize()] = Some(G_VERYLOW); 121 | table[Opcode::SWAP13.as_usize()] = Some(G_VERYLOW); 122 | table[Opcode::SWAP14.as_usize()] = Some(G_VERYLOW); 123 | table[Opcode::SWAP15.as_usize()] = Some(G_VERYLOW); 124 | table[Opcode::SWAP16.as_usize()] = Some(G_VERYLOW); 125 | 126 | table[Opcode::MUL.as_usize()] = Some(G_LOW); 127 | table[Opcode::DIV.as_usize()] = Some(G_LOW); 128 | table[Opcode::SDIV.as_usize()] = Some(G_LOW); 129 | table[Opcode::MOD.as_usize()] = Some(G_LOW); 130 | table[Opcode::SMOD.as_usize()] = Some(G_LOW); 131 | table[Opcode::SIGNEXTEND.as_usize()] = Some(G_LOW); 132 | 133 | table[Opcode::ADDMOD.as_usize()] = Some(G_MID); 134 | table[Opcode::MULMOD.as_usize()] = Some(G_MID); 135 | table[Opcode::JUMP.as_usize()] = Some(G_MID); 136 | 137 | table[Opcode::JUMPI.as_usize()] = Some(G_HIGH); 138 | table[Opcode::JUMPDEST.as_usize()] = Some(G_JUMPDEST); 139 | 140 | table 141 | }; 142 | -------------------------------------------------------------------------------- /src/standard/gasometer/costs.rs: -------------------------------------------------------------------------------- 1 | use evm_interpreter::error::ExitException; 2 | use primitive_types::{H256, U256}; 3 | 4 | use super::{consts::*, utils::log2floor}; 5 | use crate::standard::Config; 6 | 7 | pub fn call_extra_check(gas: U256, after_gas: u64, config: &Config) -> Result<(), ExitException> { 8 | if config.err_on_call_with_more_gas && U256::from(after_gas) < gas { 9 | Err(ExitException::OutOfGas) 10 | } else { 11 | Ok(()) 12 | } 13 | } 14 | 15 | pub fn suicide_refund(already_removed: bool) -> i64 { 16 | if already_removed { 17 | 0 18 | } else { 19 | R_SUICIDE 20 | } 21 | } 22 | 23 | #[allow(clippy::collapsible_else_if)] 24 | pub fn sstore_refund(original: H256, current: H256, new: H256, config: &Config) -> i64 { 25 | if config.sstore_gas_metering { 26 | if current == new { 27 | 0 28 | } else { 29 | if original == current && new == H256::default() { 30 | config.refund_sstore_clears 31 | } else { 32 | let mut refund = 0; 33 | 34 | if original != H256::default() { 35 | if current == H256::default() { 36 | refund -= config.refund_sstore_clears; 37 | } else if new == H256::default() { 38 | refund += config.refund_sstore_clears; 39 | } 40 | } 41 | 42 | if original == new { 43 | if original == H256::default() { 44 | refund += (config.gas_sstore_set - config.gas_sload) as i64; 45 | } else { 46 | refund += (config.gas_sstore_reset - config.gas_sload) as i64; 47 | } 48 | } 49 | 50 | refund 51 | } 52 | } 53 | } else { 54 | if current != H256::default() && new == H256::default() { 55 | config.refund_sstore_clears 56 | } else { 57 | 0 58 | } 59 | } 60 | } 61 | 62 | pub fn create2_cost(len: U256) -> Result { 63 | let base = U256::from(G_CREATE); 64 | // ceil(len / 32.0) 65 | let sha_addup_base = len / U256::from(32) 66 | + if len % U256::from(32) == U256::zero() { 67 | U256::zero() 68 | } else { 69 | U256::one() 70 | }; 71 | let sha_addup = U256::from(G_SHA3WORD) 72 | .checked_mul(sha_addup_base) 73 | .ok_or(ExitException::OutOfGas)?; 74 | let gas = base.checked_add(sha_addup).ok_or(ExitException::OutOfGas)?; 75 | 76 | if gas > U256::from(u64::MAX) { 77 | return Err(ExitException::OutOfGas); 78 | } 79 | 80 | Ok(gas.as_u64()) 81 | } 82 | 83 | pub fn exp_cost(power: U256, config: &Config) -> Result { 84 | if power == U256::zero() { 85 | Ok(G_EXP) 86 | } else { 87 | let gas = U256::from(G_EXP) 88 | .checked_add( 89 | U256::from(config.gas_expbyte) 90 | .checked_mul(U256::from(log2floor(power) / 8 + 1)) 91 | .ok_or(ExitException::OutOfGas)?, 92 | ) 93 | .ok_or(ExitException::OutOfGas)?; 94 | 95 | if gas > U256::from(u64::MAX) { 96 | return Err(ExitException::OutOfGas); 97 | } 98 | 99 | Ok(gas.as_u64()) 100 | } 101 | } 102 | 103 | pub fn verylowcopy_cost(len: U256) -> Result { 104 | let wordd = len / U256::from(32); 105 | let wordr = len % U256::from(32); 106 | 107 | let gas = U256::from(G_VERYLOW) 108 | .checked_add( 109 | U256::from(G_COPY) 110 | .checked_mul(if wordr == U256::zero() { 111 | wordd 112 | } else { 113 | wordd + U256::one() 114 | }) 115 | .ok_or(ExitException::OutOfGas)?, 116 | ) 117 | .ok_or(ExitException::OutOfGas)?; 118 | 119 | if gas > U256::from(u64::MAX) { 120 | return Err(ExitException::OutOfGas); 121 | } 122 | 123 | Ok(gas.as_u64()) 124 | } 125 | 126 | pub fn extcodecopy_cost(len: U256, is_cold: bool, config: &Config) -> Result { 127 | let wordd = len / U256::from(32); 128 | let wordr = len % U256::from(32); 129 | let gas = U256::from(address_access_cost(is_cold, config.gas_ext_code, config)) 130 | .checked_add( 131 | U256::from(G_COPY) 132 | .checked_mul(if wordr == U256::zero() { 133 | wordd 134 | } else { 135 | wordd + U256::one() 136 | }) 137 | .ok_or(ExitException::OutOfGas)?, 138 | ) 139 | .ok_or(ExitException::OutOfGas)?; 140 | 141 | if gas > U256::from(u64::MAX) { 142 | return Err(ExitException::OutOfGas); 143 | } 144 | 145 | Ok(gas.as_u64()) 146 | } 147 | 148 | pub fn log_cost(n: u8, len: U256) -> Result { 149 | let gas = U256::from(G_LOG) 150 | .checked_add( 151 | U256::from(G_LOGDATA) 152 | .checked_mul(len) 153 | .ok_or(ExitException::OutOfGas)?, 154 | ) 155 | .ok_or(ExitException::OutOfGas)? 156 | .checked_add(U256::from(G_LOGTOPIC * n as u64)) 157 | .ok_or(ExitException::OutOfGas)?; 158 | 159 | if gas > U256::from(u64::MAX) { 160 | return Err(ExitException::OutOfGas); 161 | } 162 | 163 | Ok(gas.as_u64()) 164 | } 165 | 166 | pub fn sha3_cost(len: U256) -> Result { 167 | let wordd = len / U256::from(32); 168 | let wordr = len % U256::from(32); 169 | 170 | let gas = U256::from(G_SHA3) 171 | .checked_add( 172 | U256::from(G_SHA3WORD) 173 | .checked_mul(if wordr == U256::zero() { 174 | wordd 175 | } else { 176 | wordd + U256::one() 177 | }) 178 | .ok_or(ExitException::OutOfGas)?, 179 | ) 180 | .ok_or(ExitException::OutOfGas)?; 181 | 182 | if gas > U256::from(u64::MAX) { 183 | return Err(ExitException::OutOfGas); 184 | } 185 | 186 | Ok(gas.as_u64()) 187 | } 188 | 189 | pub fn sload_cost(is_cold: bool, config: &Config) -> u64 { 190 | if config.increase_state_access_gas { 191 | if is_cold { 192 | config.gas_sload_cold 193 | } else { 194 | config.gas_storage_read_warm 195 | } 196 | } else { 197 | config.gas_sload 198 | } 199 | } 200 | 201 | #[allow(clippy::collapsible_else_if)] 202 | pub fn sstore_cost( 203 | original: H256, 204 | current: H256, 205 | new: H256, 206 | gas: u64, 207 | is_cold: bool, 208 | config: &Config, 209 | ) -> Result { 210 | let gas_cost = if config.sstore_gas_metering { 211 | if config.sstore_revert_under_stipend && gas <= config.call_stipend { 212 | return Err(ExitException::OutOfGas); 213 | } 214 | 215 | if new == current { 216 | config.gas_sload 217 | } else { 218 | if original == current { 219 | if original == H256::zero() { 220 | config.gas_sstore_set 221 | } else { 222 | config.gas_sstore_reset 223 | } 224 | } else { 225 | config.gas_sload 226 | } 227 | } 228 | } else { 229 | if current == H256::zero() && new != H256::zero() { 230 | config.gas_sstore_set 231 | } else { 232 | config.gas_sstore_reset 233 | } 234 | }; 235 | Ok( 236 | // In EIP-2929 we charge extra if the slot has not been used yet in this transaction 237 | if is_cold { 238 | gas_cost + config.gas_sload_cold 239 | } else { 240 | gas_cost 241 | }, 242 | ) 243 | } 244 | pub fn tload_cost(config: &Config) -> Result { 245 | Ok(config.gas_storage_read_warm) 246 | } 247 | 248 | pub fn tstore_cost(config: &Config) -> Result { 249 | Ok(config.gas_storage_read_warm) 250 | } 251 | 252 | pub fn suicide_cost(value: U256, is_cold: bool, target_exists: bool, config: &Config) -> u64 { 253 | let eip161 = !config.empty_considered_exists; 254 | let should_charge_topup = if eip161 { 255 | value != U256::zero() && !target_exists 256 | } else { 257 | !target_exists 258 | }; 259 | 260 | let suicide_gas_topup = if should_charge_topup { 261 | config.gas_suicide_new_account 262 | } else { 263 | 0 264 | }; 265 | 266 | let mut gas = config.gas_suicide + suicide_gas_topup; 267 | if config.increase_state_access_gas && is_cold { 268 | gas += config.gas_account_access_cold 269 | } 270 | gas 271 | } 272 | 273 | pub fn call_cost( 274 | value: U256, 275 | is_cold: bool, 276 | is_call_or_callcode: bool, 277 | is_call_or_staticcall: bool, 278 | new_account: bool, 279 | config: &Config, 280 | ) -> u64 { 281 | let transfers_value = value != U256::default(); 282 | address_access_cost(is_cold, config.gas_call, config) 283 | + xfer_cost(is_call_or_callcode, transfers_value) 284 | + new_cost(is_call_or_staticcall, new_account, transfers_value, config) 285 | } 286 | 287 | pub fn address_access_cost(is_cold: bool, regular_value: u64, config: &Config) -> u64 { 288 | if config.increase_state_access_gas { 289 | if is_cold { 290 | config.gas_account_access_cold 291 | } else { 292 | config.gas_storage_read_warm 293 | } 294 | } else { 295 | regular_value 296 | } 297 | } 298 | 299 | fn xfer_cost(is_call_or_callcode: bool, transfers_value: bool) -> u64 { 300 | if is_call_or_callcode && transfers_value { 301 | G_CALLVALUE 302 | } else { 303 | 0 304 | } 305 | } 306 | 307 | fn new_cost( 308 | is_call_or_staticcall: bool, 309 | new_account: bool, 310 | transfers_value: bool, 311 | config: &Config, 312 | ) -> u64 { 313 | let eip161 = !config.empty_considered_exists; 314 | if is_call_or_staticcall { 315 | if eip161 { 316 | if transfers_value && new_account { 317 | G_NEWACCOUNT 318 | } else { 319 | 0 320 | } 321 | } else if new_account { 322 | G_NEWACCOUNT 323 | } else { 324 | 0 325 | } 326 | } else { 327 | 0 328 | } 329 | } 330 | 331 | pub fn memory_gas(a: usize) -> Result { 332 | let a = a as u64; 333 | G_MEMORY 334 | .checked_mul(a) 335 | .ok_or(ExitException::OutOfGas)? 336 | .checked_add(a.checked_mul(a).ok_or(ExitException::OutOfGas)? / 512) 337 | .ok_or(ExitException::OutOfGas) 338 | } 339 | -------------------------------------------------------------------------------- /src/standard/gasometer/utils.rs: -------------------------------------------------------------------------------- 1 | use primitive_types::U256; 2 | 3 | pub fn log2floor(value: U256) -> u64 { 4 | assert_ne!(value, U256::zero()); 5 | let mut l: u64 = 256; 6 | for i in 0..4 { 7 | let i = 3 - i; 8 | if value.0[i] == 0u64 { 9 | l -= 64; 10 | } else { 11 | l -= value.0[i].leading_zeros() as u64; 12 | if l == 0 { 13 | return l; 14 | } else { 15 | return l - 1; 16 | } 17 | } 18 | } 19 | l 20 | } 21 | -------------------------------------------------------------------------------- /src/standard/invoker/resolver.rs: -------------------------------------------------------------------------------- 1 | use alloc::{rc::Rc, vec::Vec}; 2 | 3 | use evm_interpreter::{ 4 | error::{ExitError, ExitResult}, 5 | etable::EtableSet, 6 | machine::Machine, 7 | runtime::{RuntimeBackend, RuntimeState}, 8 | EtableInterpreter, Interpreter, 9 | }; 10 | use primitive_types::H160; 11 | 12 | use crate::{invoker::InvokerControl, standard::Config}; 13 | 14 | /// A code resolver. 15 | /// 16 | /// The resolver handles how a call (with the target code address) or create 17 | /// (with the init code) is turned into a colored machine. The resolver can 18 | /// construct a machine, pushing the call stack, or directly exit, handling a 19 | /// precompile. 20 | pub trait Resolver { 21 | type State; 22 | type Interpreter: Interpreter; 23 | 24 | /// Resolve a call (with the target code address). 25 | #[allow(clippy::type_complexity)] 26 | fn resolve_call( 27 | &self, 28 | code_address: H160, 29 | input: Vec, 30 | state: Self::State, 31 | handler: &mut H, 32 | ) -> Result))>, ExitError>; 33 | 34 | /// Resolve a create (with the init code). 35 | #[allow(clippy::type_complexity)] 36 | fn resolve_create( 37 | &self, 38 | init_code: Vec, 39 | state: Self::State, 40 | handler: &mut H, 41 | ) -> Result))>, ExitError>; 42 | } 43 | 44 | /// A set of precompiles. 45 | pub trait PrecompileSet { 46 | /// Attempt to execute the precompile at the given `code_address`. Returns 47 | /// `None` if it's not a precompile. 48 | fn execute( 49 | &self, 50 | code_address: H160, 51 | input: &[u8], 52 | state: &mut S, 53 | handler: &mut H, 54 | ) -> Option<(ExitResult, Vec)>; 55 | } 56 | 57 | impl PrecompileSet for () { 58 | fn execute( 59 | &self, 60 | _code_address: H160, 61 | _input: &[u8], 62 | _state: &mut S, 63 | _handler: &mut H, 64 | ) -> Option<(ExitResult, Vec)> { 65 | None 66 | } 67 | } 68 | 69 | /// The standard code resolver where the color is an [Etable]. This is usually 70 | /// what you need. 71 | pub struct EtableResolver<'config, 'precompile, 'etable, Pre, ES> { 72 | config: &'config Config, 73 | etable: &'etable ES, 74 | precompiles: &'precompile Pre, 75 | } 76 | 77 | impl<'config, 'precompile, 'etable, Pre, ES> 78 | EtableResolver<'config, 'precompile, 'etable, Pre, ES> 79 | { 80 | pub fn new( 81 | config: &'config Config, 82 | precompiles: &'precompile Pre, 83 | etable: &'etable ES, 84 | ) -> Self { 85 | Self { 86 | config, 87 | precompiles, 88 | etable, 89 | } 90 | } 91 | } 92 | 93 | impl<'config, 'precompile, 'etable, H, Pre, ES> Resolver 94 | for EtableResolver<'config, 'precompile, 'etable, Pre, ES> 95 | where 96 | ES::State: AsRef + AsMut, 97 | H: RuntimeBackend, 98 | Pre: PrecompileSet, 99 | ES: EtableSet, 100 | { 101 | type State = ES::State; 102 | type Interpreter = EtableInterpreter<'etable, ES>; 103 | 104 | /// Resolve a call (with the target code address). 105 | #[allow(clippy::type_complexity)] 106 | fn resolve_call( 107 | &self, 108 | code_address: H160, 109 | input: Vec, 110 | mut state: ES::State, 111 | handler: &mut H, 112 | ) -> Result))>, ExitError> { 113 | if let Some((r, retval)) = 114 | self.precompiles 115 | .execute(code_address, &input, &mut state, handler) 116 | { 117 | return Ok(InvokerControl::DirectExit((r, (state, retval)))); 118 | } 119 | 120 | let code = handler.code(code_address); 121 | 122 | let machine = Machine::::new( 123 | Rc::new(code), 124 | Rc::new(input), 125 | self.config.stack_limit, 126 | self.config.memory_limit, 127 | state, 128 | ); 129 | 130 | let ret = InvokerControl::Enter(EtableInterpreter::new(machine, self.etable)); 131 | 132 | Ok(ret) 133 | } 134 | 135 | /// Resolve a create (with the init code). 136 | #[allow(clippy::type_complexity)] 137 | fn resolve_create( 138 | &self, 139 | init_code: Vec, 140 | state: ES::State, 141 | _handler: &mut H, 142 | ) -> Result))>, ExitError> { 143 | let machine = Machine::new( 144 | Rc::new(init_code), 145 | Rc::new(Vec::new()), 146 | self.config.stack_limit, 147 | self.config.memory_limit, 148 | state, 149 | ); 150 | 151 | let ret = InvokerControl::Enter(EtableInterpreter::new(machine, self.etable)); 152 | 153 | Ok(ret) 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/standard/invoker/routines.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | 3 | use evm_interpreter::{ 4 | error::{CallTrapData, CreateTrapData, ExitError, ExitException, ExitResult}, 5 | opcode::Opcode, 6 | runtime::{RuntimeBackend, RuntimeEnvironment, RuntimeState, SetCodeOrigin, Transfer}, 7 | }; 8 | use primitive_types::{H160, U256}; 9 | 10 | use crate::{ 11 | backend::TransactionalBackend, 12 | invoker::InvokerControl, 13 | standard::{Config, InvokerState, Resolver, SubstackInvoke}, 14 | MergeStrategy, 15 | }; 16 | 17 | #[allow(clippy::too_many_arguments, clippy::type_complexity)] 18 | pub fn make_enter_call_machine( 19 | _config: &Config, 20 | resolver: &R, 21 | code_address: H160, 22 | input: Vec, 23 | transfer: Option, 24 | state: R::State, 25 | handler: &mut H, 26 | ) -> Result))>, ExitError> 27 | where 28 | R::State: AsRef, 29 | H: RuntimeEnvironment + RuntimeBackend + TransactionalBackend, 30 | R: Resolver, 31 | { 32 | handler.mark_hot(state.as_ref().context.address, None); 33 | 34 | if let Some(transfer) = transfer { 35 | handler.transfer(transfer)?; 36 | } 37 | 38 | resolver.resolve_call(code_address, input, state, handler) 39 | } 40 | 41 | #[allow(clippy::type_complexity, clippy::too_many_arguments)] 42 | pub fn make_enter_create_machine( 43 | config: &Config, 44 | resolver: &R, 45 | caller: H160, 46 | init_code: Vec, 47 | transfer: Transfer, 48 | state: R::State, 49 | handler: &mut H, 50 | ) -> Result))>, ExitError> 51 | where 52 | R::State: AsRef, 53 | H: RuntimeEnvironment + RuntimeBackend + TransactionalBackend, 54 | R: Resolver, 55 | { 56 | if let Some(limit) = config.max_initcode_size { 57 | if init_code.len() > limit { 58 | return Err(ExitException::CreateContractLimit.into()); 59 | } 60 | } 61 | 62 | handler.mark_hot(caller, None); 63 | handler.mark_hot(state.as_ref().context.address, None); 64 | 65 | handler.transfer(transfer)?; 66 | 67 | if handler.code_size(state.as_ref().context.address) != U256::zero() 68 | || handler.nonce(state.as_ref().context.address) > U256::zero() 69 | { 70 | return Err(ExitException::CreateCollision.into()); 71 | } 72 | handler.inc_nonce(caller)?; 73 | if config.create_increase_nonce { 74 | handler.inc_nonce(state.as_ref().context.address)?; 75 | } 76 | 77 | handler.reset_storage(state.as_ref().context.address); 78 | handler.mark_create(state.as_ref().context.address); 79 | 80 | resolver.resolve_create(init_code, state, handler) 81 | } 82 | 83 | #[allow(clippy::type_complexity, clippy::too_many_arguments)] 84 | pub fn enter_call_substack( 85 | config: &Config, 86 | resolver: &R, 87 | trap_data: CallTrapData, 88 | code_address: H160, 89 | state: R::State, 90 | handler: &mut H, 91 | ) -> Result< 92 | ( 93 | SubstackInvoke, 94 | InvokerControl))>, 95 | ), 96 | ExitError, 97 | > 98 | where 99 | R::State: AsRef, 100 | H: RuntimeEnvironment + RuntimeBackend + TransactionalBackend, 101 | R: Resolver, 102 | { 103 | handler.push_substate(); 104 | 105 | let work = || -> Result<(SubstackInvoke, _), ExitError> { 106 | let machine = make_enter_call_machine( 107 | config, 108 | resolver, 109 | code_address, 110 | trap_data.input.clone(), 111 | trap_data.transfer.clone(), 112 | state, 113 | handler, 114 | )?; 115 | 116 | Ok((SubstackInvoke::Call { trap: trap_data }, machine)) 117 | }; 118 | 119 | match work() { 120 | Ok(machine) => Ok(machine), 121 | Err(err) => { 122 | handler.pop_substate(MergeStrategy::Discard); 123 | Err(err) 124 | } 125 | } 126 | } 127 | 128 | #[allow(clippy::type_complexity, clippy::too_many_arguments)] 129 | pub fn enter_create_substack( 130 | config: &Config, 131 | resolver: &R, 132 | code: Vec, 133 | trap_data: CreateTrapData, 134 | state: R::State, 135 | handler: &mut H, 136 | ) -> Result< 137 | ( 138 | SubstackInvoke, 139 | InvokerControl))>, 140 | ), 141 | ExitError, 142 | > 143 | where 144 | R::State: AsRef, 145 | H: RuntimeEnvironment + RuntimeBackend + TransactionalBackend, 146 | R: Resolver, 147 | { 148 | handler.push_substate(); 149 | 150 | let work = || -> Result<(SubstackInvoke, InvokerControl))>), ExitError> { 151 | let CreateTrapData { 152 | scheme, 153 | value, 154 | code: _, 155 | } = trap_data.clone(); 156 | 157 | let caller = scheme.caller(); 158 | let address = scheme.address(handler); 159 | 160 | let transfer = Transfer { 161 | source: caller, 162 | target: address, 163 | value, 164 | }; 165 | 166 | let machine = make_enter_create_machine( 167 | config, resolver, caller, code, transfer, state, handler, 168 | )?; 169 | 170 | Ok(( 171 | SubstackInvoke::Create { 172 | address, 173 | trap: trap_data, 174 | }, 175 | machine, 176 | )) 177 | }; 178 | 179 | match work() { 180 | Ok(machine) => Ok(machine), 181 | Err(err) => { 182 | handler.pop_substate(MergeStrategy::Discard); 183 | Err(err) 184 | } 185 | } 186 | } 187 | 188 | fn check_first_byte(config: &Config, code: &[u8]) -> Result<(), ExitError> { 189 | if config.disallow_executable_format && Some(&Opcode::EOFMAGIC.as_u8()) == code.first() { 190 | return Err(ExitException::InvalidOpcode(Opcode::EOFMAGIC).into()); 191 | } 192 | Ok(()) 193 | } 194 | 195 | pub fn deploy_create_code<'config, S, H>( 196 | config: &Config, 197 | address: H160, 198 | retbuf: Vec, 199 | state: &mut S, 200 | handler: &mut H, 201 | origin: SetCodeOrigin, 202 | ) -> Result<(), ExitError> 203 | where 204 | S: InvokerState<'config>, 205 | H: RuntimeEnvironment + RuntimeBackend + TransactionalBackend, 206 | { 207 | check_first_byte(config, &retbuf[..])?; 208 | 209 | if let Some(limit) = config.create_contract_limit { 210 | if retbuf.len() > limit { 211 | return Err(ExitException::CreateContractLimit.into()); 212 | } 213 | } 214 | 215 | state.record_codedeposit(retbuf.len())?; 216 | 217 | handler.set_code(address, retbuf, origin)?; 218 | 219 | Ok(()) 220 | } 221 | -------------------------------------------------------------------------------- /src/standard/invoker/state.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | 3 | use evm_interpreter::{ 4 | error::ExitError, 5 | runtime::{GasState, RuntimeState}, 6 | }; 7 | use primitive_types::{H160, H256, U256}; 8 | 9 | use crate::{standard::Config, MergeStrategy}; 10 | 11 | pub trait InvokerState<'config>: GasState + Sized { 12 | fn new_transact_call( 13 | runtime: RuntimeState, 14 | gas_limit: U256, 15 | data: &[u8], 16 | access_list: &[(H160, Vec)], 17 | config: &'config Config, 18 | ) -> Result; 19 | fn new_transact_create( 20 | runtime: RuntimeState, 21 | gas_limit: U256, 22 | code: &[u8], 23 | access_list: &[(H160, Vec)], 24 | config: &'config Config, 25 | ) -> Result; 26 | 27 | fn substate( 28 | &mut self, 29 | runtime: RuntimeState, 30 | gas_limit: U256, 31 | is_static: bool, 32 | call_has_value: bool, 33 | ) -> Result; 34 | fn merge(&mut self, substate: Self, strategy: MergeStrategy); 35 | 36 | fn record_codedeposit(&mut self, len: usize) -> Result<(), ExitError>; 37 | 38 | fn is_static(&self) -> bool; 39 | fn effective_gas(&self) -> U256; 40 | fn config(&self) -> &Config; 41 | } 42 | -------------------------------------------------------------------------------- /src/standard/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Standard machines and gasometers 2 | //! 3 | //! This module implements the standard configurations of the interpreter, like how it works on 4 | //! Ethereum mainnet. Most of them can still be customized to add additional functionality, by 5 | //! wrapping them or replacing the generic parameters. 6 | 7 | mod config; 8 | mod gasometer; 9 | mod invoker; 10 | 11 | use alloc::vec::Vec; 12 | 13 | use evm_interpreter::{ 14 | error::{CallCreateTrap, ExitError}, 15 | etable, machine, 16 | runtime::{GasState, RuntimeState}, 17 | }; 18 | use primitive_types::{H160, H256, U256}; 19 | 20 | pub use self::{ 21 | config::Config, 22 | gasometer::{eval as eval_gasometer, GasometerState}, 23 | invoker::{ 24 | routines, EtableResolver, IntoCallCreateTrap, Invoker, InvokerState, PrecompileSet, 25 | Resolver, SubstackInvoke, TransactArgs, TransactInvoke, TransactValue, 26 | }, 27 | }; 28 | use crate::{gasometer::GasMutState, MergeStrategy}; 29 | 30 | /// Standard machine. 31 | pub type Machine<'config> = machine::Machine>; 32 | 33 | /// Standard Etable opcode handle function. 34 | pub type Efn<'config, H> = etable::Efn, H, CallCreateTrap>; 35 | 36 | /// Standard Etable. 37 | pub type Etable<'config, H, F = Efn<'config, H>> = 38 | etable::Etable, H, CallCreateTrap, F>; 39 | 40 | pub struct State<'config> { 41 | pub runtime: RuntimeState, 42 | pub gasometer: GasometerState<'config>, 43 | } 44 | 45 | impl<'config> AsRef for State<'config> { 46 | fn as_ref(&self) -> &RuntimeState { 47 | &self.runtime 48 | } 49 | } 50 | 51 | impl<'config> AsMut for State<'config> { 52 | fn as_mut(&mut self) -> &mut RuntimeState { 53 | &mut self.runtime 54 | } 55 | } 56 | 57 | impl<'config> AsRef> for State<'config> { 58 | fn as_ref(&self) -> &GasometerState<'config> { 59 | &self.gasometer 60 | } 61 | } 62 | 63 | impl<'config> AsMut> for State<'config> { 64 | fn as_mut(&mut self) -> &mut GasometerState<'config> { 65 | &mut self.gasometer 66 | } 67 | } 68 | 69 | impl<'config> GasState for State<'config> { 70 | fn gas(&self) -> U256 { 71 | self.gasometer.gas() 72 | } 73 | } 74 | 75 | impl<'config> GasMutState for State<'config> { 76 | fn record_gas(&mut self, gas: U256) -> Result<(), ExitError> { 77 | self.gasometer.record_gas(gas) 78 | } 79 | } 80 | 81 | impl<'config> InvokerState<'config> for State<'config> { 82 | fn new_transact_call( 83 | runtime: RuntimeState, 84 | gas_limit: U256, 85 | data: &[u8], 86 | access_list: &[(H160, Vec)], 87 | config: &'config Config, 88 | ) -> Result { 89 | Ok(Self { 90 | runtime, 91 | gasometer: GasometerState::new_transact_call(gas_limit, data, access_list, config)?, 92 | }) 93 | } 94 | fn new_transact_create( 95 | runtime: RuntimeState, 96 | gas_limit: U256, 97 | code: &[u8], 98 | access_list: &[(H160, Vec)], 99 | config: &'config Config, 100 | ) -> Result { 101 | Ok(Self { 102 | runtime, 103 | gasometer: GasometerState::new_transact_create(gas_limit, code, access_list, config)?, 104 | }) 105 | } 106 | 107 | fn substate( 108 | &mut self, 109 | runtime: RuntimeState, 110 | gas_limit: U256, 111 | is_static: bool, 112 | call_has_value: bool, 113 | ) -> Result { 114 | Ok(Self { 115 | runtime, 116 | gasometer: self 117 | .gasometer 118 | .submeter(gas_limit, is_static, call_has_value)?, 119 | }) 120 | } 121 | fn merge(&mut self, substate: Self, strategy: MergeStrategy) { 122 | self.gasometer.merge(substate.gasometer, strategy) 123 | } 124 | 125 | fn record_codedeposit(&mut self, len: usize) -> Result<(), ExitError> { 126 | self.gasometer.record_codedeposit(len) 127 | } 128 | 129 | fn is_static(&self) -> bool { 130 | self.gasometer.is_static 131 | } 132 | fn effective_gas(&self) -> U256 { 133 | self.gasometer.effective_gas() 134 | } 135 | fn config(&self) -> &Config { 136 | self.gasometer.config 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /tests/contract/DeployAndDestroy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | contract DeployAndDestroy { 5 | constructor() { 6 | selfdestruct(payable(msg.sender)); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tests/contract/SimpleContract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | contract SimpleContract { 5 | address public owner; 6 | 7 | // Constructor sets the owner of the contract 8 | constructor() { 9 | owner = msg.sender; 10 | } 11 | 12 | // Function to destroy the contract and send the remaining funds to the target address 13 | function destroy(address target) public { 14 | selfdestruct(payable(target)); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/contract/deploy_and_destroy_init_code: -------------------------------------------------------------------------------- 1 | 6080604052348015600e575f80fd5b503373ffffffffffffffffffffffffffffffffffffffff16fffe 2 | -------------------------------------------------------------------------------- /tests/contract/simple_contract_bytecode.txt: -------------------------------------------------------------------------------- 1 | 608060405234801561000f575f80fd5b50335f806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506101948061005c5f395ff3fe608060405234801561000f575f80fd5b5060043610610033575f3560e01c8062f55d9d146100375780638da5cb5b14610053575b5f80fd5b610051600480360381019061004c919061010b565b610071565b005b61005b61008a565b6040516100689190610145565b60405180910390f35b8073ffffffffffffffffffffffffffffffffffffffff16ff5b5f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6100da826100b1565b9050919050565b6100ea816100d0565b81146100f4575f80fd5b50565b5f81359050610105816100e1565b92915050565b5f602082840312156101205761011f6100ad565b5b5f61012d848285016100f7565b91505092915050565b61013f816100d0565b82525050565b5f6020820190506101585f830184610136565b9291505056fea26469706673582212201e4165398f9671b365261ccc1129042cbcea13db557b5359b2298c51f2ab274d64736f6c63430008150033 2 | -------------------------------------------------------------------------------- /tests/eip_6780.rs: -------------------------------------------------------------------------------- 1 | mod mock; 2 | use evm::{ 3 | backend::{OverlayedBackend, RuntimeBaseBackend}, 4 | interpreter::{ 5 | error::ExitError, 6 | etable::{Chained, Single}, 7 | }, 8 | standard::{Config, Etable, EtableResolver, Invoker, TransactArgs, TransactValue}, 9 | }; 10 | use mock::{MockAccount, MockBackend}; 11 | use primitive_types::{H160, H256, U256}; 12 | 13 | const SIMPLE_CONTRACT_INITCODE: &str = include_str!("./contract/simple_contract_bytecode.txt"); 14 | const DEPLOY_AND_DESTROY_INITCODE: &str = include_str!("./contract/deploy_and_destroy_init_code"); 15 | 16 | fn transact( 17 | config: &Config, 18 | args: TransactArgs, 19 | overlayed_backend: &mut OverlayedBackend, 20 | ) -> Result { 21 | let gas_etable = Single::new(evm::standard::eval_gasometer); 22 | let exec_etable = Etable::runtime(); 23 | let etable = Chained(gas_etable, exec_etable); 24 | let resolver = EtableResolver::new(config, &(), &etable); 25 | let invoker = Invoker::new(config, &resolver); 26 | 27 | evm::transact(args.clone(), Some(4), overlayed_backend, &invoker) 28 | } 29 | 30 | #[test] 31 | fn self_destruct_before_cancun() { 32 | let mut backend = MockBackend::default(); 33 | backend.state.insert( 34 | H160::from_low_u64_be(1), 35 | MockAccount { 36 | balance: U256::from(1_000_000_000), 37 | code: vec![], 38 | nonce: U256::one(), 39 | storage: Default::default(), 40 | transient_storage: Default::default(), 41 | }, 42 | ); 43 | let config = Config::shanghai(); 44 | let mut overlayed_backend = OverlayedBackend::new(backend, Default::default(), &config); 45 | 46 | let init_code = hex::decode(SIMPLE_CONTRACT_INITCODE.trim_end()).unwrap(); 47 | let args = TransactArgs::Create { 48 | caller: H160::from_low_u64_be(1), 49 | value: U256::zero(), 50 | init_code, 51 | salt: Some(H256::from_low_u64_be(4)), 52 | gas_limit: U256::from(400_000), 53 | gas_price: U256::from(1), 54 | access_list: vec![], 55 | }; 56 | 57 | // Create simple contract 58 | let contract_address = match transact(&config, args, &mut overlayed_backend) { 59 | Ok(TransactValue::Create { address, .. }) => address, 60 | _ => panic!("Failed to create contract"), 61 | }; 62 | 63 | // Verify contract creation 64 | assert!(!overlayed_backend.code(contract_address).is_empty()); 65 | assert_eq!(overlayed_backend.nonce(contract_address), U256::one()); 66 | 67 | // Apply overlayed changeset 68 | let (mut backend, changeset) = overlayed_backend.deconstruct(); 69 | backend.apply_overlayed(&changeset); 70 | 71 | // Call Self destruct in anothor transaction 72 | let mut overlayed_backend = OverlayedBackend::new(backend, Default::default(), &config); 73 | let args = TransactArgs::Call { 74 | caller: H160::from_low_u64_be(1), 75 | address: contract_address, 76 | value: U256::zero(), 77 | data: hex::decode( 78 | "00f55d9d00000000000000000000000055c41626c84445180eda39bac564606c633dd980", 79 | ) 80 | .unwrap(), 81 | gas_limit: U256::from(400_000), 82 | gas_price: U256::one(), 83 | access_list: vec![], 84 | }; 85 | 86 | let result = transact(&config, args, &mut overlayed_backend); 87 | let changeset = overlayed_backend.deconstruct().1; 88 | 89 | assert!(result.is_ok()); 90 | assert!(changeset.deletes.contains(&contract_address)); 91 | } 92 | 93 | #[test] 94 | fn self_destruct_cancun() { 95 | let mut backend = MockBackend::default(); 96 | backend.state.insert( 97 | H160::from_low_u64_be(1), 98 | MockAccount { 99 | balance: U256::from(1_000_000_000), 100 | code: vec![], 101 | nonce: U256::one(), 102 | storage: Default::default(), 103 | transient_storage: Default::default(), 104 | }, 105 | ); 106 | let config = Config::cancun(); 107 | let mut overlayed_backend = OverlayedBackend::new(backend, Default::default(), &config); 108 | 109 | let init_code = 110 | hex::decode(SIMPLE_CONTRACT_INITCODE.trim_end()).expect("Failed to decode contract"); 111 | let args = TransactArgs::Create { 112 | caller: H160::from_low_u64_be(1), 113 | value: U256::zero(), 114 | init_code, 115 | salt: Some(H256::from_low_u64_be(4)), 116 | gas_limit: U256::from(400_000), 117 | gas_price: U256::from(1), 118 | access_list: vec![], 119 | }; 120 | 121 | // Create simple contract 122 | let contract_address = match transact(&config, args, &mut overlayed_backend) { 123 | Ok(TransactValue::Create { address, .. }) => address, 124 | _ => panic!("Failed to create contract"), 125 | }; 126 | 127 | // Verify contract creation 128 | assert!(!overlayed_backend.code(contract_address).is_empty()); 129 | assert_eq!(overlayed_backend.nonce(contract_address), U256::one()); 130 | 131 | // Apply overlayed changeset 132 | let (mut backend, changeset) = overlayed_backend.deconstruct(); 133 | backend.apply_overlayed(&changeset); 134 | 135 | let mut overlayed_backend = OverlayedBackend::new(backend, Default::default(), &config); 136 | // Self destruct contract in another transaction 137 | let args = TransactArgs::Call { 138 | caller: H160::from_low_u64_be(1), 139 | address: contract_address, 140 | value: U256::zero(), 141 | data: hex::decode( 142 | "00f55d9d00000000000000000000000055c41626c84445180eda39bac564606c633dd980", 143 | ) 144 | .unwrap(), 145 | gas_limit: U256::from(400_000), 146 | gas_price: U256::one(), 147 | access_list: vec![], 148 | }; 149 | 150 | let result = transact(&config, args, &mut overlayed_backend); 151 | let changeset = overlayed_backend.deconstruct().1; 152 | 153 | assert!(result.is_ok()); 154 | assert!(!changeset.deletes.contains(&contract_address)); 155 | } 156 | 157 | #[test] 158 | fn self_destruct_same_tx_cancun() { 159 | let mut backend = MockBackend::default(); 160 | backend.state.insert( 161 | H160::from_low_u64_be(1), 162 | MockAccount { 163 | balance: U256::from(1_000_000_000), 164 | code: vec![], 165 | nonce: U256::one(), 166 | storage: Default::default(), 167 | transient_storage: Default::default(), 168 | }, 169 | ); 170 | let config = Config::cancun(); 171 | let mut overlayed_backend = OverlayedBackend::new(backend, Default::default(), &config); 172 | 173 | let init_code = 174 | hex::decode(DEPLOY_AND_DESTROY_INITCODE.trim_end()).expect("Failed to decode contract"); 175 | let args = TransactArgs::Create { 176 | caller: H160::from_low_u64_be(1), 177 | value: U256::zero(), 178 | init_code, 179 | salt: Some(H256::from_low_u64_be(4)), 180 | gas_limit: U256::from(400_000), 181 | gas_price: U256::from(1), 182 | access_list: vec![], 183 | }; 184 | 185 | // Create deploy and destroy contract 186 | let result = transact(&config, args, &mut overlayed_backend); 187 | assert!(result.is_ok()); 188 | 189 | // Verify contract was deleted 190 | assert!(!overlayed_backend.deconstruct().1.deletes.is_empty()); 191 | } 192 | -------------------------------------------------------------------------------- /tests/mock.rs: -------------------------------------------------------------------------------- 1 | extern crate evm; 2 | 3 | use evm::backend::{OverlayedChangeSet, RuntimeBaseBackend, RuntimeEnvironment}; 4 | use primitive_types::{H160, H256, U256}; 5 | use std::collections::BTreeMap; 6 | 7 | #[derive(Default, Clone, Debug)] 8 | pub struct MockAccount { 9 | pub balance: U256, 10 | pub code: Vec, 11 | pub nonce: U256, 12 | pub storage: BTreeMap, 13 | pub transient_storage: BTreeMap, 14 | } 15 | 16 | #[derive(Clone, Debug, Default)] 17 | pub struct MockBackend { 18 | pub state: BTreeMap, 19 | } 20 | 21 | impl MockBackend { 22 | pub fn apply_overlayed(&mut self, changeset: &OverlayedChangeSet) { 23 | for (address, balance) in changeset.balances.clone() { 24 | self.state.entry(address).or_default().balance = balance; 25 | } 26 | 27 | for (address, code) in changeset.codes.clone() { 28 | self.state.entry(address).or_default().code = code; 29 | } 30 | 31 | for (address, nonce) in changeset.nonces.clone() { 32 | self.state.entry(address).or_default().nonce = nonce; 33 | } 34 | 35 | for address in changeset.storage_resets.clone() { 36 | self.state.entry(address).or_default().storage = BTreeMap::new(); 37 | } 38 | 39 | for ((address, key), value) in changeset.storages.clone() { 40 | let account = self.state.entry(address).or_default(); 41 | 42 | if value == H256::default() { 43 | account.storage.remove(&key); 44 | } else { 45 | account.storage.insert(key, value); 46 | } 47 | } 48 | 49 | for address in changeset.deletes.clone() { 50 | self.state.remove(&address); 51 | } 52 | } 53 | } 54 | 55 | impl RuntimeEnvironment for MockBackend { 56 | fn block_hash(&self, _number: U256) -> H256 { 57 | Default::default() 58 | } 59 | 60 | fn block_number(&self) -> U256 { 61 | Default::default() 62 | } 63 | 64 | fn block_coinbase(&self) -> H160 { 65 | Default::default() 66 | } 67 | 68 | fn block_timestamp(&self) -> U256 { 69 | Default::default() 70 | } 71 | 72 | fn block_difficulty(&self) -> U256 { 73 | Default::default() 74 | } 75 | 76 | fn block_randomness(&self) -> Option { 77 | Default::default() 78 | } 79 | 80 | fn block_gas_limit(&self) -> U256 { 81 | Default::default() 82 | } 83 | 84 | fn block_base_fee_per_gas(&self) -> U256 { 85 | Default::default() 86 | } 87 | 88 | fn chain_id(&self) -> U256 { 89 | Default::default() 90 | } 91 | } 92 | 93 | impl RuntimeBaseBackend for MockBackend { 94 | fn balance(&self, address: H160) -> U256 { 95 | self.state 96 | .get(&address) 97 | .cloned() 98 | .unwrap_or(Default::default()) 99 | .balance 100 | } 101 | 102 | fn code(&self, address: H160) -> Vec { 103 | self.state 104 | .get(&address) 105 | .cloned() 106 | .unwrap_or(Default::default()) 107 | .code 108 | } 109 | 110 | fn exists(&self, address: H160) -> bool { 111 | self.state.contains_key(&address) 112 | } 113 | 114 | fn storage(&self, address: H160, index: H256) -> H256 { 115 | self.state 116 | .get(&address) 117 | .cloned() 118 | .unwrap_or(Default::default()) 119 | .storage 120 | .get(&index) 121 | .cloned() 122 | .unwrap_or(H256::default()) 123 | } 124 | 125 | fn transient_storage(&self, address: H160, index: H256) -> H256 { 126 | self.state 127 | .get(&address) 128 | .cloned() 129 | .unwrap_or(Default::default()) 130 | .transient_storage 131 | .get(&index) 132 | .cloned() 133 | .unwrap_or(H256::default()) 134 | } 135 | 136 | fn nonce(&self, address: H160) -> U256 { 137 | self.state 138 | .get(&address) 139 | .cloned() 140 | .unwrap_or(Default::default()) 141 | .nonce 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /tracer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "evm-tracer" 3 | version = "0.0.0-dev" 4 | edition = { workspace = true } 5 | rust-version = { workspace = true } 6 | license = { workspace = true } 7 | authors = { workspace = true } 8 | repository = { workspace = true } 9 | keywords = { workspace = true } 10 | 11 | [dependencies] 12 | evm = { path = ".." } 13 | -------------------------------------------------------------------------------- /tracer/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod standard; 2 | 3 | use evm::interpreter::{machine::Machine, opcode::Opcode}; 4 | 5 | pub trait EvalTracer { 6 | fn on_eval(&mut self, machine: &Machine, handle: &H, opcode: Opcode, position: usize); 7 | } 8 | -------------------------------------------------------------------------------- /tracer/src/standard.rs: -------------------------------------------------------------------------------- 1 | use evm::{ 2 | interpreter::opcode::Opcode, 3 | standard::{Machine, State}, 4 | }; 5 | 6 | pub trait EvalTracer { 7 | fn on_eval(&mut self, machine: &Machine, handle: &H, opcode: Opcode, position: usize); 8 | } 9 | 10 | impl<'config, H, T: EvalTracer> crate::EvalTracer, H> for T { 11 | fn on_eval(&mut self, machine: &Machine, handle: &H, opcode: Opcode, position: usize) { 12 | EvalTracer::::on_eval(self, machine, handle, opcode, position) 13 | } 14 | } 15 | --------------------------------------------------------------------------------