diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml index f8d94ed..266c8a2 100644 --- a/.github/workflows/develop.yml +++ b/.github/workflows/develop.yml @@ -12,6 +12,7 @@ on: env: CARGO_TERM_COLOR: always SSH_AUTH_SOCK: /tmp/ssh_agent.sock + TINYEVM_CI_TESTS: true jobs: Test: @@ -23,9 +24,10 @@ jobs: with: submodules: 'recursive' - - uses: actions-rs/toolchain@v1 # Rust toolchain + - name: Set up Rust toolchain + uses: dtolnay/rust-toolchain@stable with: - components: rustfmt, clippy + components: rustfmt, clippy, llvm-tools-preview - name: Get OS infomration id: os @@ -53,28 +55,18 @@ jobs: - name: Install Python dependencies run: pip install -r requirements-dev.txt - - uses: webfactory/ssh-agent@v0.5.4 - with: - ssh-private-key: | - ${{ secrets.SSH_PRIVATE_KEY_RUST_EVM }} - ${{ secrets.SSH_PRIVATE_KEY_REVM }} - - name: Check code formatting run: cargo +stable fmt - name: Check code linting - run: cargo clippy --tests --benches --features linting + run: cargo clippy --all-targets --all-features -- -D warnings - name: Check documentation linting - run: cargo doc --no-deps --features linting + run: cargo doc --no-deps --all-features - name: Run Rust tests run: cargo nextest run - - uses: dtolnay/rust-toolchain@stable - with: - components: llvm-tools-preview - - name: Install as local Python package uses: PyO3/maturin-action@v1 with: diff --git a/Cargo.lock b/Cargo.lock index 3b90efa..4ef0954 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -65,36 +65,529 @@ version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +[[package]] +name = "alloy" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ba1c79677c9ce51c8d45e20845b05e6fb070ea2c863fba03ad6af2c778474bd" +dependencies = [ + "alloy-consensus", + "alloy-contract", + "alloy-core", + "alloy-eips", + "alloy-genesis", + "alloy-network", + "alloy-provider", + "alloy-pubsub", + "alloy-rpc-client", + "alloy-rpc-types", + "alloy-serde", + "alloy-signer", + "alloy-signer-local", + "alloy-transport", + "alloy-transport-http", + "alloy-transport-ipc", + "alloy-transport-ws", +] + +[[package]] +name = "alloy-chains" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1752d7d62e2665da650a36d84abbf239f812534475d51f072a49a533513b7cdd" +dependencies = [ + "num_enum", + "strum", +] + +[[package]] +name = "alloy-consensus" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da374e868f54c7f4ad2ad56829827badca388efd645f8cf5fccc61c2b5343504" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "c-kzg", + "serde", +] + +[[package]] +name = "alloy-contract" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e47b2a620fd588d463ccf0f5931b41357664b293a8d31592768845a2a101bb9e" +dependencies = [ + "alloy-dyn-abi", + "alloy-json-abi", + "alloy-network", + "alloy-primitives", + "alloy-provider", + "alloy-pubsub", + "alloy-rpc-types-eth", + "alloy-sol-types", + "alloy-transport", + "futures", + "futures-util", + "thiserror", +] + +[[package]] +name = "alloy-core" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "529fc6310dc1126c8de51c376cbc59c79c7f662bd742be7dc67055d5421a81b4" +dependencies = [ + "alloy-dyn-abi", + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-types", +] + +[[package]] +name = "alloy-dyn-abi" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413902aa18a97569e60f679c23f46a18db1656d87ab4d4e49d0e1e52042f66df" +dependencies = [ + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-type-parser", + "alloy-sol-types", + "const-hex", + "itoa", + "serde", + "serde_json", + "winnow 0.6.13", +] + +[[package]] +name = "alloy-eips" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f76ecab54890cdea1e4808fc0891c7e6cfcf71fe1a9fe26810c7280ef768f4ed" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "c-kzg", + "derive_more", + "once_cell", + "serde", + "sha2", +] + +[[package]] +name = "alloy-genesis" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bca15afde1b6d15e3fc1c97421262b1bbb37aee45752e3c8b6d6f13f776554ff" +dependencies = [ + "alloy-primitives", + "alloy-serde", + "serde", +] + +[[package]] +name = "alloy-json-abi" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc05b04ac331a9f07e3a4036ef7926e49a8bf84a99a1ccfc7e2ab55a5fcbb372" +dependencies = [ + "alloy-primitives", + "alloy-sol-type-parser", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-json-rpc" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d6f34930b7e3e2744bcc79056c217f00cb2abb33bc5d4ff88da7623c5bb078b" +dependencies = [ + "alloy-primitives", + "serde", + "serde_json", + "thiserror", + "tracing", +] + +[[package]] +name = "alloy-network" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25f6895fc31b48fa12306ef9b4f78b7764f8bd6d7d91cdb0a40e233704a0f23f" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-json-rpc", + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-serde", + "alloy-signer", + "alloy-sol-types", + "async-trait", + "auto_impl", + "futures-utils-wasm", + "thiserror", +] + [[package]] name = "alloy-primitives" -version = "0.7.6" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccb3ead547f4532bc8af961649942f0b9c16ee9226e26caa3f38420651cc0bf4" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if", + "const-hex", + "derive_more", + "hex-literal", + "itoa", + "k256", + "keccak-asm", + "proptest", + "rand", + "ruint", + "serde", + "tiny-keccak", +] + +[[package]] +name = "alloy-provider" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c538bfa893d07e27cb4f3c1ab5f451592b7c526d511d62b576a2ce59e146e4a" +dependencies = [ + "alloy-chains", + "alloy-consensus", + "alloy-eips", + "alloy-json-rpc", + "alloy-network", + "alloy-primitives", + "alloy-pubsub", + "alloy-rpc-client", + "alloy-rpc-types-eth", + "alloy-transport", + "alloy-transport-http", + "alloy-transport-ipc", + "alloy-transport-ws", + "async-stream", + "async-trait", + "auto_impl", + "dashmap", + "futures", + "futures-utils-wasm", + "lru", + "pin-project", + "reqwest 0.12.5", + "serde", + "serde_json", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "alloy-pubsub" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702f330b7da123a71465ab9d39616292f8344a2811c28f2cc8d8438a69d79e35" +dependencies = [ + "alloy-json-rpc", + "alloy-primitives", + "alloy-transport", + "bimap", + "futures", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tower", + "tracing", +] + +[[package]] +name = "alloy-rlp" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a43b18702501396fa9bcdeecd533bc85fac75150d308fc0f6800a01e6234a003" +dependencies = [ + "alloy-rlp-derive", + "arrayvec", + "bytes", +] + +[[package]] +name = "alloy-rlp-derive" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d83524c1f6162fcb5b0decf775498a125066c86dda6066ed609531b0e912f85a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.68", +] + +[[package]] +name = "alloy-rpc-client" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ba31bae67773fd5a60020bea900231f8396202b7feca4d0c70c6b59308ab4a8" +dependencies = [ + "alloy-json-rpc", + "alloy-primitives", + "alloy-pubsub", + "alloy-transport", + "alloy-transport-http", + "alloy-transport-ipc", + "alloy-transport-ws", + "futures", + "pin-project", + "reqwest 0.12.5", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tower", + "tracing", + "url", +] + +[[package]] +name = "alloy-rpc-types" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f2fbe956a3e0f0975c798f488dc6be96b669544df3737e18f4a325b42f4c86" +dependencies = [ + "alloy-rpc-types-engine", + "alloy-rpc-types-eth", + "alloy-serde", +] + +[[package]] +name = "alloy-rpc-types-engine" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd473d98ec552f8229cd6d566bd2b0bbfc5bb4efcefbb5288c834aa8fd832020" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-rpc-types-eth", + "alloy-serde", + "jsonwebtoken 9.3.0", + "rand", + "serde", + "thiserror", +] + +[[package]] +name = "alloy-rpc-types-eth" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab4123ee21f99ba4bd31bfa36ba89112a18a500f8b452f02b35708b1b951e2b9" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "alloy-sol-types", + "itertools 0.13.0", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "alloy-serde" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9416c52959e66ead795a11f4a86c248410e9e368a0765710e57055b8a1774dd6" +dependencies = [ + "alloy-primitives", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-signer" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b33753c09fa1ad85e5b092b8dc2372f1e337a42e84b9b4cff9fede75ba4adb32" +dependencies = [ + "alloy-primitives", + "async-trait", + "auto_impl", + "elliptic-curve", + "k256", + "thiserror", +] + +[[package]] +name = "alloy-signer-local" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40a37dc216c269b8a7244047cb1c18a9c69f7a0332ab2c4c2aa4cbb1a31468b" +dependencies = [ + "alloy-consensus", + "alloy-network", + "alloy-primitives", + "alloy-signer", + "async-trait", + "k256", + "rand", + "thiserror", +] + +[[package]] +name = "alloy-sol-macro" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b40397ddcdcc266f59f959770f601ce1280e699a91fc1862f29cef91707cd09" +dependencies = [ + "alloy-sol-macro-expander", + "alloy-sol-macro-input", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.68", +] + +[[package]] +name = "alloy-sol-macro-expander" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "867a5469d61480fea08c7333ffeca52d5b621f5ca2e44f271b117ec1fc9a0525" +dependencies = [ + "alloy-json-abi", + "alloy-sol-macro-input", + "const-hex", + "heck", + "indexmap", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.68", + "syn-solidity", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-macro-input" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e482dc33a32b6fadbc0f599adea520bd3aaa585c141a80b404d0a3e3fa72528" +dependencies = [ + "alloy-json-abi", + "const-hex", + "dunce", + "heck", + "proc-macro2", + "quote", + "serde_json", + "syn 2.0.68", + "syn-solidity", +] + +[[package]] +name = "alloy-sol-type-parser" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbcba3ca07cf7975f15d871b721fb18031eec8bce51103907f6dcce00b255d98" +dependencies = [ + "serde", + "winnow 0.6.13", +] + +[[package]] +name = "alloy-sol-types" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a91ca40fa20793ae9c3841b83e74569d1cc9af29a2f5237314fd3452d51e38c7" +dependencies = [ + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-macro", + "const-hex", + "serde", +] + +[[package]] +name = "alloy-transport" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01b51a291f949f755e6165c3ed562883175c97423703703355f4faa4b7d0a57c" +dependencies = [ + "alloy-json-rpc", + "base64 0.22.1", + "futures-util", + "futures-utils-wasm", + "serde", + "serde_json", + "thiserror", + "tokio", + "tower", + "tracing", + "url", +] + +[[package]] +name = "alloy-transport-http" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86d65871f9f1cafe1ed25cde2f1303be83e6473e995a2d56c275ae4fcce6119c" +dependencies = [ + "alloy-json-rpc", + "alloy-transport", + "reqwest 0.12.5", + "serde_json", + "tower", + "tracing", + "url", +] + +[[package]] +name = "alloy-transport-ipc" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f783611babedbbe90db3478c120fb5f5daacceffc210b39adc0af4fe0da70bad" +checksum = "173cefa110afac7a53cf2e75519327761f2344d305eea2993f3af1b2c1fc1c44" dependencies = [ - "alloy-rlp", + "alloy-json-rpc", + "alloy-pubsub", + "alloy-transport", "bytes", - "cfg-if", - "const-hex", - "derive_more", - "hex-literal", - "itoa", - "k256", - "keccak-asm", - "proptest", - "rand", - "ruint", - "serde", - "tiny-keccak", + "futures", + "interprocess", + "pin-project", + "serde_json", + "tokio", + "tokio-util", + "tracing", ] [[package]] -name = "alloy-rlp" -version = "0.3.7" +name = "alloy-transport-ws" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a43b18702501396fa9bcdeecd533bc85fac75150d308fc0f6800a01e6234a003" +checksum = "9c0aff8af5be5e58856c5cdd1e46db2c67c7ecd3a652d9100b4822c96c899947" dependencies = [ - "arrayvec", - "bytes", + "alloy-pubsub", + "alloy-transport", + "futures", + "http 1.1.0", + "rustls 0.23.11", + "serde_json", + "tokio", + "tokio-tungstenite 0.23.1", + "tracing", + "ws_stream_wasm", ] [[package]] @@ -127,7 +620,7 @@ dependencies = [ "ark-std 0.4.0", "derivative", "digest 0.10.7", - "itertools", + "itertools 0.10.5", "num-bigint", "num-traits", "paste", @@ -227,6 +720,28 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.68", +] + [[package]] name = "async-trait" version = "0.1.80" @@ -338,6 +853,12 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" +[[package]] +name = "bimap" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "230c5f1ca6a325a32553f8640d31ac9b49f2411e901e427570154868b46da4f7" + [[package]] name = "bit-set" version = "0.5.3" @@ -666,7 +1187,7 @@ dependencies = [ "clap", "criterion-plot", "csv", - "itertools", + "itertools 0.10.5", "lazy_static", "num-traits", "oorandom", @@ -688,7 +1209,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876" dependencies = [ "cast", - "itertools", + "itertools 0.10.5", ] [[package]] @@ -774,6 +1295,19 @@ dependencies = [ "cipher", ] +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "data-encoding" version = "2.6.0" @@ -844,6 +1378,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "doctest-file" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aac81fa3e28d21450aa4d2ac065992ba96a1d7303efbce51a95f4fd175b67562" + [[package]] name = "dotenv" version = "0.15.0" @@ -974,7 +1514,7 @@ dependencies = [ "sha2", "sha3", "thiserror", - "uuid", + "uuid 0.8.2", ] [[package]] @@ -1147,7 +1687,7 @@ dependencies = [ "futures-locks", "futures-util", "instant", - "reqwest", + "reqwest 0.11.27", "serde", "serde_json", "thiserror", @@ -1174,17 +1714,17 @@ dependencies = [ "futures-timer", "futures-util", "hashers", - "http", + "http 0.2.12", "instant", - "jsonwebtoken", + "jsonwebtoken 8.3.0", "once_cell", "pin-project", - "reqwest", + "reqwest 0.11.27", "serde", "serde_json", "thiserror", "tokio", - "tokio-tungstenite", + "tokio-tungstenite 0.20.1", "tracing", "tracing-futures", "url", @@ -1267,6 +1807,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -1391,6 +1946,12 @@ dependencies = [ "slab", ] +[[package]] +name = "futures-utils-wasm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" + [[package]] name = "fxhash" version = "0.2.1" @@ -1418,8 +1979,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -1468,7 +2031,7 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", + "http 0.2.12", "indexmap", "slab", "tokio", @@ -1558,6 +2121,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http-body" version = "0.4.6" @@ -1565,7 +2139,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", - "http", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +dependencies = [ + "bytes", + "http 1.1.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", "pin-project-lite", ] @@ -1592,8 +2189,8 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http", - "http-body", + "http 0.2.12", + "http-body 0.4.6", "httparse", "httpdate", "itoa", @@ -1605,6 +2202,25 @@ dependencies = [ "want", ] +[[package]] +name = "hyper" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + [[package]] name = "hyper-rustls" version = "0.24.2" @@ -1612,11 +2228,47 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", - "http", - "hyper", - "rustls", + "http 0.2.12", + "hyper 0.14.29", + "rustls 0.21.12", + "tokio", + "tokio-rustls 0.24.1", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper 1.4.1", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "hyper 1.4.1", + "pin-project-lite", + "socket2", "tokio", - "tokio-rustls", + "tower", + "tower-service", + "tracing", ] [[package]] @@ -1707,6 +2359,21 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "interprocess" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67bafc2f5dbdad79a6d925649758d5472647b416028099f0b829d1b67fdd47d3" +dependencies = [ + "doctest-file", + "futures-core", + "libc", + "recvmsg", + "tokio", + "widestring", + "windows-sys 0.52.0", +] + [[package]] name = "ipnet" version = "2.9.0" @@ -1722,6 +2389,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -1744,13 +2420,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" dependencies = [ "base64 0.21.7", - "pem", + "pem 1.1.1", "ring 0.16.20", "serde", "serde_json", "simple_asn1", ] +[[package]] +name = "jsonwebtoken" +version = "9.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ae10193d25051e74945f1ea2d0b42e03cc3b890f7e4cc5faa44997d808193f" +dependencies = [ + "base64 0.21.7", + "js-sys", + "pem 3.0.4", + "ring 0.17.8", + "serde", + "serde_json", + "simple_asn1", +] + [[package]] name = "k256" version = "0.13.3" @@ -1827,6 +2518,15 @@ version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +[[package]] +name = "lru" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" +dependencies = [ + "hashbrown", +] + [[package]] name = "maplit" version = "1.0.2" @@ -1883,6 +2583,23 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -2059,6 +2776,50 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "openssl" +version = "0.10.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.68", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "overload" version = "0.1.1" @@ -2148,6 +2909,16 @@ dependencies = [ "base64 0.13.1", ] +[[package]] +name = "pem" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" +dependencies = [ + "base64 0.22.1", + "serde", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -2217,6 +2988,12 @@ dependencies = [ "spki", ] +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + [[package]] name = "plotters" version = "0.3.6" @@ -2296,6 +3073,30 @@ dependencies = [ "toml_edit 0.21.1", ] +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.86" @@ -2471,6 +3272,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "recvmsg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175" + [[package]] name = "redis" version = "0.25.4" @@ -2551,9 +3358,9 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http", - "http-body", - "hyper", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.29", "hyper-rustls", "ipnet", "js-sys", @@ -2562,22 +3369,61 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls", - "rustls-pemfile", + "rustls 0.21.12", + "rustls-pemfile 1.0.4", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 0.1.2", "system-configuration", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.1", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots 0.25.4", + "winreg 0.50.0", +] + +[[package]] +name = "reqwest" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-core", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "hyper 1.4.1", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile 2.1.2", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 1.0.1", + "tokio", + "tokio-native-tls", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots", - "winreg", + "winreg 0.52.0", ] [[package]] @@ -2796,10 +3642,24 @@ checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", "ring 0.17.8", - "rustls-webpki", + "rustls-webpki 0.101.7", "sct", ] +[[package]] +name = "rustls" +version = "0.23.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4828ea528154ae444e5a642dbb7d5623354030dc9822b83fd9bb79683c7399d0" +dependencies = [ + "once_cell", + "ring 0.17.8", + "rustls-pki-types", + "rustls-webpki 0.102.5", + "subtle", + "zeroize", +] + [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -2809,6 +3669,22 @@ dependencies = [ "base64 0.21.7", ] +[[package]] +name = "rustls-pemfile" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +dependencies = [ + "base64 0.22.1", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" + [[package]] name = "rustls-webpki" version = "0.101.7" @@ -2819,6 +3695,17 @@ dependencies = [ "untrusted 0.9.0", ] +[[package]] +name = "rustls-webpki" +version = "0.102.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a6fccd794a42c2c105b513a2f62bc3fd8f3ba57a4593677ceb0bd035164d78" +dependencies = [ + "ring 0.17.8", + "rustls-pki-types", + "untrusted 0.9.0", +] + [[package]] name = "rustversion" version = "1.0.17" @@ -2885,6 +3772,15 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -2946,6 +3842,29 @@ dependencies = [ "cc", ] +[[package]] +name = "security-framework" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" +dependencies = [ + "bitflags 2.6.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" version = "0.11.0" @@ -3264,12 +4183,30 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn-solidity" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c837dc8852cb7074e46b444afb81783140dab12c58867b49fb3898fbafedf7ea" +dependencies = [ + "paste", + "proc-macro2", + "quote", + "syn 2.0.68", +] + [[package]] name = "sync_wrapper" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" + [[package]] name = "system-configuration" version = "0.5.1" @@ -3315,40 +4252,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "tevm" -version = "0.1.0" -dependencies = [ - "criterion", - "dotenv", - "ethers", - "ethers-contract", - "ethers-core", - "ethers-middleware", - "ethers-providers", - "eyre", - "hashbrown", - "hex", - "lazy_static", - "libc", - "maplit", - "num-bigint", - "primitive-types", - "pyo3", - "redis", - "revm", - "ruint", - "serde", - "serde_json", - "sha3", - "simple_logger", - "strum_macros", - "thread_local", - "tokio", - "tracing", - "tracing-subscriber", -] - [[package]] name = "textwrap" version = "0.11.0" @@ -3439,6 +4342,42 @@ dependencies = [ "crunchy", ] +[[package]] +name = "tinyevm" +version = "1.0.0" +dependencies = [ + "alloy", + "criterion", + "dotenv", + "ethers", + "ethers-contract", + "ethers-core", + "ethers-middleware", + "ethers-providers", + "eyre", + "hashbrown", + "hex", + "lazy_static", + "libc", + "maplit", + "num-bigint", + "primitive-types", + "pyo3", + "redis", + "revm", + "ruint", + "serde", + "serde_json", + "sha3", + "simple_logger", + "strum_macros", + "thread_local", + "tokio", + "tracing", + "tracing-subscriber", + "uuid 1.10.0", +] + [[package]] name = "tinytemplate" version = "1.2.1" @@ -3494,14 +4433,47 @@ dependencies = [ "syn 2.0.68", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls", + "rustls 0.21.12", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls 0.23.11", + "rustls-pki-types", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +dependencies = [ + "futures-core", + "pin-project-lite", "tokio", + "tokio-util", ] [[package]] @@ -3512,11 +4484,27 @@ checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" dependencies = [ "futures-util", "log", - "rustls", + "rustls 0.21.12", + "tokio", + "tokio-rustls 0.24.1", + "tungstenite 0.20.1", + "webpki-roots 0.25.4", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6989540ced10490aaf14e6bad2e3d33728a2813310a0c71d1574304c49631cd" +dependencies = [ + "futures-util", + "log", + "rustls 0.23.11", + "rustls-pki-types", "tokio", - "tokio-rustls", - "tungstenite", - "webpki-roots", + "tokio-rustls 0.26.0", + "tungstenite 0.23.0", + "webpki-roots 0.26.3", ] [[package]] @@ -3577,6 +4565,28 @@ dependencies = [ "winnow 0.6.13", ] +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + [[package]] name = "tower-service" version = "0.3.2" @@ -3589,6 +4599,7 @@ version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ + "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -3669,17 +4680,37 @@ dependencies = [ "byteorder", "bytes", "data-encoding", - "http", + "http 0.2.12", "httparse", "log", "rand", - "rustls", + "rustls 0.21.12", "sha1", "thiserror", "url", "utf-8", ] +[[package]] +name = "tungstenite" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e2ce1e47ed2994fd43b04c8f618008d4cabdd5ee34027cf14f9d918edd9c8" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http 1.1.0", + "httparse", + "log", + "rand", + "rustls 0.23.11", + "rustls-pki-types", + "sha1", + "thiserror", + "utf-8", +] + [[package]] name = "typenum" version = "1.17.0" @@ -3788,12 +4819,27 @@ dependencies = [ "serde", ] +[[package]] +name = "uuid" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +dependencies = [ + "getrandom", +] + [[package]] name = "valuable" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.4" @@ -3916,6 +4962,21 @@ version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +[[package]] +name = "webpki-roots" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "widestring" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" + [[package]] name = "winapi" version = "0.3.9" @@ -4114,6 +5175,16 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "winreg" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "ws_stream_wasm" version = "0.7.4" diff --git a/Cargo.toml b/Cargo.toml index 6d04358..9077022 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,11 @@ [package] -name = "tevm" -version = "0.1.0" +name = "tinyevm" +version = "1.0.0" edition = "2021" [lib] name="tinyevm" -crate_type = ["cdylib", "rlib"] +crate-type = ["cdylib", "rlib"] path="src/lib.rs" [dependencies] @@ -36,10 +36,31 @@ tokio = { version = "1.38.0", features = ["full"] } strum_macros = "0.26.4" hashbrown = "*" redis = { version= "0.25.4", optional = true} +alloy = { version = "0.1.4", features = ["full"] } +uuid = { version = "1.9.1", features = ["v4"] } [dev-dependencies] criterion = {version="0.3.6", features=["html_reports"] } [features] default = [] -redis=["dep:redis"] \ No newline at end of file +redis=["dep:redis"] + +[[bench]] +name = "general" +harness = false + +[[bench]] +name = "deploy" +harness = false + +[[bench]] +name = "infinite_loop" +harness = false + +[[bench]] +name = "function" +harness = false + +[profile.bench] +debug = true \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..06e62e7 --- /dev/null +++ b/Makefile @@ -0,0 +1,15 @@ +prepare: + cargo install cargo-criterion + pip install -r requirements-dev.txt +linting: + cargo clippy --tests --benches --all-features -- -D warnings +build: + maturin build --release -i 3.9 +test: + cargo nextest run --no-fail-fast --success-output=never + maturin develop --release + pytest -s +bench: + cargo bench +clean: + cargo clean diff --git a/README.md b/README.md index 89b53e5..877baf3 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,201 @@ -# tevm +# tinyevm -An EVM with instrumentation for debugging and analysis using REVM. +Dynamic library providing API to EVM executor + + +## Start development + +* Clone this repository + +``` bash +git clone git@github.com:sbip-sg/tevm.git +``` + +* Run test in tinyevm + +- For unit tests, run + +``` bash +make test +``` + +- For Rust APIs benchmark test, run + +``` bash +make bench +``` + + +## How to test the underline REVM + + + + +## How to contribute + +* Clone this repository + +``` bash +git clone git@github.com:sbip-sg/tinyevm.git +``` + +* Create a fork for the task you are working on + + +``` bash +cd tinyevm +git checkout -b [name-of-your-fork] +``` + +Continue working on this fork, commit as often as you like. When you +feel like it's ready to merge, create a PR on github to request +merging your fork to `develop`. + +After PR is appproved, please merge your changes to `develop`: + * If there is no conflict, you can merge the changes directly on github + * In case there are any conflicts preventing the merge, follow these steps + * `git fetch --all` + * `git merge origin/develop` + * Resolve all the conflicts, after testing, make another commit and you can continue merging the changes. + * After squashing and mering the PR, delete your branch. + + +## Sample usage + +``` python +# todo add a python example +``` + +You can find example usage in Python in the `example/` folder. + +Example files: + +``` text +example/ +├── C.hex # Bytecode for contract C as hex +├── example-bug-detected.py # Sample bug-detection in Python +├── example.py # Sample code in Python +├── int_cast_0.5.0.hex # Bytecode for contract IntCast as hex compiled with solc version 0.5.0 +├── int_cast_0.8.0.hex # Bytecode for contract IntCast as hex compiled with solc version 0.8.0 +├── int_cast.sol # Source code for the IntCast contract +├── tx_origin.hex # Bytecode for contract TxOrigin as hex compiled with solc version 0.8.12 +├── tx_origin.sol # Source code for the TxOrigin contract +├── calls_trace.hex # Bytecode for contract `sample` as hex compiled with solc version 0.7.0 +├── call_trace.sol # Source code for the `sample` contract for call tracing test +├── block_hash.sol # Source code for the `BHash` contract for blockhash test +├── block_hash.hex # Bytecode for contract `BHash` as hex compiled with solc version 0.7.0 +├── test_tod.sol # Source code for testing SSLOAD/SSTORE instrumentation +├── test_tod.hex # Bytecode for the tet contract in test_tod.sol +├── balance.sol # Source code for the `Balance` contract for balance get/set test +├── balance.hex # Bytecode for contract `Balance` as hex compiled with solc version 0.7.0 +├── self_destruct.sol # Source code for the `Des` contract for SELFDESTRUCT detection +├── self_destruct.hex # Bytecode for contract `Des` as hex compiled with solc 0.8.10 +├── contract_creation.sol # Source code for testing code coverage calculated with program counter +├── contract_creation_B.hex # Bytecode for contract `B` in contract_creation.sol +├── deploy_with_args_and_value.hex # Bytecode for contract DeployWithArgsAndValue as hex compiled with solc version 0.7.0 +├── deploy_with_args_and_value.sol # Source code for the `DeployWithArgsAndValue` contract for testing contract with constructor arguments +├── ... +``` + +The contract `C` used in this example is compiled from [data_structures.sol](https://github.com/cassc/evm-play/tree/main/contracts). + + +## Python Module + +### Local development + +* Clean up the previous build + + ``` bash + make clean + ``` + +* Install development dependencies + + ``` bash + pip install -r requirements-dev.txt + ``` + +* Build and install the Python library + + ``` bash + maturing develop + ``` +* Run test, this will run the Rust test first, install the development version of TinyEVM and run the Python tests + + ``` bash + make test + ``` + +### Build and release Python library + +* The following command will build a `whl` file inside `target/wheels` folder + ``` bash + maturin build --release + # or to compile for python 3.9 + maturin build --release -i 3.9 + ``` +* You can install this file with `pip` command + ``` bash + pip install target/wheels/*.whl --force-reinstall + ``` + +Here's the corrected version: + +### Cache Web3 Requests to Redis + +By default, requests to the Web3 endpoints are cached in the file system. If you wish to use Redis as the cache, compile with the `provider_cache_redis` flag: + +```bash +maturin build --release -i 3.9 --cargo-extra-args="--features provider_cache_redis" +``` + +Additionally, you must set the environment variable `TINYEVM_REDIS_NODE` to a valid Redis endpoint. + +# Benchmarks + +## Global snapshot benchmarks + +To run the test: + +``` bash +maturing develop --release +pytest -s --show-capture all tests/test_global_snapshot.py +``` + + +* Results from legacy Tinyevm (using instrumented REVM) commit `839b0b8822702b49096bc2bf3f092c7a1aab13a3`: + +``` text +============================================================================================================= test session starts ============================================================================================================== +platform linux -- Python 3.9.7, pytest-7.2.2, pluggy-1.0.0 +benchmark: 4.0.0 (defaults: timer=time.perf_counter disable_gc=False min_rounds=5 min_time=0.000005 max_time=1.0 calibration_precision=10 warmup=False warmup_iterations=100000) +rootdir: /home/garfield/projects/sbip-sg/tinyevm +plugins: cov-4.1.0, web3-5.31.2, hypothesis-6.82.0, anyio-3.6.2, benchmark-4.0.0, xdist-3.3.1 +collected 5 items + +... +... + +------------------------------------------------------------------------------------------------------------- benchmark: 4 tests ------------------------------------------------------------------------------------------------------------ +Name (time in ms) Min Max Mean StdDev Median IQR Outliers OPS Rounds Iterations +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +test_global_snapshot[x_fns2-True-fastseq no snapshot] 118.6619 (1.0) 124.2569 (1.0) 122.1113 (1.0) 1.7266 (1.0) 122.0048 (1.0) 1.8421 (1.0) 2;1 8.1892 (1.0) 8 1 +test_global_snapshot[x_fns0-False-fastseq take and restore snapshot] 123.2069 (1.04) 131.6260 (1.06) 126.0777 (1.03) 3.4155 (1.98) 124.7658 (1.02) 5.4766 (2.97) 2;0 7.9316 (0.97) 8 1 +test_global_snapshot[x_fns1-False-slowseq take and restore snapshot] 448.7344 (3.78) 490.6087 (3.95) 466.8714 (3.82) 18.1452 (10.51) 456.7645 (3.74) 29.2119 (15.86) 1;0 2.1419 (0.26) 5 1 +test_global_snapshot[x_fns3-True-slowseq no snapshot] 453.6979 (3.82) 516.7671 (4.16) 473.7815 (3.88) 25.0584 (14.51) 463.8226 (3.80) 25.1137 (13.63) 1;0 2.1107 (0.26) 5 1 +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +``` + +* Results from the current branch: + +``` text +------------------------------------------------------------------------------------------------------------ benchmark: 4 tests ------------------------------------------------------------------------------------------------------------ +Name (time in ms) Min Max Mean StdDev Median IQR Outliers OPS Rounds Iterations +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +test_global_snapshot[x_fns2-True-fastseq no snapshot] 95.1790 (1.0) 101.4989 (1.0) 97.1787 (1.0) 2.3811 (1.0) 96.0393 (1.0) 2.9408 (1.0) 2;0 10.2903 (1.0) 10 1 +test_global_snapshot[x_fns0-False-fastseq take and restore snapshot] 101.7576 (1.07) 111.5388 (1.10) 106.1005 (1.09) 2.9359 (1.23) 106.2893 (1.11) 4.1448 (1.41) 3;0 9.4250 (0.92) 10 1 +test_global_snapshot[x_fns1-False-slowseq take and restore snapshot] 315.4810 (3.31) 337.3184 (3.32) 327.1796 (3.37) 8.0853 (3.40) 329.3743 (3.43) 9.8087 (3.34) 2;0 3.0564 (0.30) 5 1 +test_global_snapshot[x_fns3-True-slowseq no snapshot] 315.5748 (3.32) 328.8318 (3.24) 321.7635 (3.31) 5.0368 (2.12) 321.1631 (3.34) 7.1114 (2.42) 2;0 3.1079 (0.30) 5 1 +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +``` diff --git a/benches/complex.rs b/benches/complex.rs new file mode 100644 index 0000000..d5b10ad --- /dev/null +++ b/benches/complex.rs @@ -0,0 +1,44 @@ +use std::time::Duration; + +use criterion::{criterion_group, criterion_main, Criterion}; +use revm::primitives::Address; +use tinyevm::{fn_sig_to_prefix, UZERO}; + +const OWNER: Address = Address::repeat_byte(0x01); +const DEPLOY_TO_ADDRESS: Address = Address::repeat_byte(0x02); + +#[allow(unused)] +fn bench_call_complex_function(c: &mut Criterion) { + c.bench_function("call_complex_function", |b| { + let source = include_str!("../tests/contracts/complex_contract.hex"); + let bytecode = hex::decode(source).unwrap(); + let mut exe = tinyevm::TinyEVM::default(); + let owner = OWNER; + let deploy_to_address = Some(DEPLOY_TO_ADDRESS); + + let resp = { + exe.deploy_helper(owner, bytecode, UZERO, None, deploy_to_address) + .unwrap() + }; + + assert!(resp.success, "Contract deploy should succeed."); + let address = Address::from_slice(&resp.data); + + let fn_sig = "complexFunction()"; + b.iter(|| { + let data = hex::decode(fn_sig_to_prefix(fn_sig)).unwrap(); + + let r = exe.contract_call_helper(address, owner, data, UZERO, None); + // assert!(r.success); // this function can revert sometimes + assert!(r.gas_usage > 0); + }) + }); +} + +criterion_group!( + name = complex; + config = Criterion::default().measurement_time(Duration::from_secs(10)); + targets = bench_call_complex_function, +); + +criterion_main!(complex); diff --git a/benches/deploy.rs b/benches/deploy.rs new file mode 100644 index 0000000..45a6177 --- /dev/null +++ b/benches/deploy.rs @@ -0,0 +1,62 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use primitive_types::H256; +use revm::primitives::Address; +use ruint::aliases::U256; +use tinyevm::{TinyEVM, UZERO}; + +const OWNER: Address = Address::repeat_byte(0x01); +const DEPLOY_TO_ADDRESS: Address = Address::repeat_byte(0x02); + +// Reusing can be slower because there are more data inside the instrumentation log +fn bench_contract_deterministic_deploy(c: &mut Criterion) { + c.bench_function("deploy_contract_deterministic", |b| { + let source = include_str!("../tests/contracts/calls_trace.hex"); + let source = hex::decode(source).unwrap(); + let mut exe = TinyEVM::default(); + + b.iter(|| { + { + exe.deploy_helper(OWNER, source.clone(), UZERO, None, Some(DEPLOY_TO_ADDRESS)) + .unwrap(); + }; + }) + }); +} + +fn bench_contract_deploy_on_different_executors(c: &mut Criterion) { + c.bench_function("deploy_contract_deploy_on_different_executors", |b| { + let source = include_str!("../tests/contracts/calls_trace.hex"); + let source = hex::decode(source).unwrap(); + + b.iter(|| { + let mut exe = TinyEVM::default(); + { + exe.deploy_helper( + OWNER, + source.clone(), + U256::from(0), + None, + Some(DEPLOY_TO_ADDRESS), + ) + .unwrap(); + }; + }) + }); +} + +#[allow(unused)] +fn bench_random_h256(c: &mut Criterion) { + c.bench_function("call H256 random", |b| { + b.iter(|| { + let _ = H256::random(); + }) + }); +} + +criterion_group!( + name = deploy; + config = Criterion::default(); + targets = bench_random_h256, bench_contract_deterministic_deploy, bench_contract_deploy_on_different_executors +); + +criterion_main!(deploy); diff --git a/benches/function.rs b/benches/function.rs new file mode 100644 index 0000000..8f10a24 --- /dev/null +++ b/benches/function.rs @@ -0,0 +1,82 @@ +use std::{iter::repeat_with, time::Duration}; + +use criterion::{criterion_group, criterion_main, Criterion}; +use primitive_types::H256; +use revm::primitives::Address; +use tinyevm::{fn_sig_to_prefix, TinyEVM, UZERO}; + +const OWNER: Address = Address::repeat_byte(0x01); +const DEPLOY_TO_ADDRESS: Address = Address::repeat_byte(0x02); + +#[allow(unused)] +fn bench_call_function_returning_large_string(c: &mut Criterion) { + c.bench_function("call_function_returning_large_string", |b| { + let source = include_str!("../tests/contracts/VeLogo.hex"); + let bytecode = hex::decode(source).unwrap(); + let mut exe = TinyEVM::default(); + + let resp = { + exe.deploy_helper(OWNER, bytecode, UZERO, None, Some(DEPLOY_TO_ADDRESS)) + .unwrap() + }; + + assert!(resp.success, "Contract deploy should succeed."); + let address = Address::from_slice(&resp.data); + + let fn_sig = "tokenURI(uint256,uint256,uint256,uint256)"; + b.iter(|| { + let fn_args_hex: String = repeat_with(H256::random).take(4).map(hex::encode).collect(); + + let add_hex = format!("{}{}", fn_sig_to_prefix(fn_sig), fn_args_hex); + + let data = hex::decode(add_hex).unwrap(); + + let r = exe.contract_call_helper(address, OWNER, data, UZERO, None); + assert!(r.success); + }) + }); +} + +#[allow(unused)] +// TODO this repeats most part of the previous test function, refactor +fn bench_call_function_returning_large_string_no_instrumentation(c: &mut Criterion) { + c.bench_function( + "call_function_returning_large_string_no_instrumetation", + |b| { + let source = include_str!("../tests/contracts/VeLogo.hex"); + let bytecode = hex::decode(source).unwrap(); + let mut exe = TinyEVM::default(); + exe.instrument_config_mut().enabled = false; + + let resp = { + exe.deploy_helper(OWNER, bytecode, UZERO, None, Some(DEPLOY_TO_ADDRESS)) + .unwrap() + }; + + assert!(resp.success, "Contract deploy should succeed."); + let address = Address::from_slice(&resp.data); + + let fn_sig = "tokenURI(uint256,uint256,uint256,uint256)"; + b.iter(|| { + let fn_args_hex: String = + repeat_with(H256::random).take(4).map(hex::encode).collect(); + + let add_hex = format!("{}{}", fn_sig_to_prefix(fn_sig), fn_args_hex); + + let data = hex::decode(add_hex).unwrap(); + + let r = exe.contract_call_helper(address, OWNER, data, UZERO, None); + assert!(r.success); + }) + }, + ); +} + +criterion_group!( + name = evm_benches; + config = Criterion::default().measurement_time(Duration::from_secs(10)); + targets = bench_call_function_returning_large_string, + bench_call_function_returning_large_string_no_instrumentation, +); + +criterion_main!(evm_benches); diff --git a/benches/general.rs b/benches/general.rs new file mode 100644 index 0000000..34c15c4 --- /dev/null +++ b/benches/general.rs @@ -0,0 +1,69 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use revm::primitives::Address; +use tinyevm::{fn_sig_to_prefix, TinyEVM, UZERO}; + +const OWNER: Address = Address::repeat_byte(0x01); +const DEPLOY_TO_ADDRESS: Address = Address::repeat_byte(0x02); + +#[allow(unused)] +fn bench_call_tracing_with_shared_executor(c: &mut Criterion) { + c.bench_function("call_tracing_with_shared_executor", |b| { + let source = include_str!("../tests/contracts/calls_trace.hex"); + let bytecode = hex::decode(source).unwrap(); + let fn_sig = "test_call_success_success_failed()"; + let fn_args_hex = ""; + let add_hex = format!("{}{}", fn_sig_to_prefix(fn_sig), fn_args_hex); + + let data = hex::decode(add_hex).unwrap(); + let mut exe = TinyEVM::default(); + + let resp = { + exe.deploy_helper(OWNER, bytecode, UZERO, None, Some(DEPLOY_TO_ADDRESS)) + .unwrap() + }; + + assert!(resp.success, "Contract deploy should succeed."); + let address = Address::from_slice(&resp.data); + + b.iter(|| { + let _ = exe.contract_call_helper(address, OWNER, data.clone(), UZERO, None); + }) + }); +} + +#[allow(unused)] +fn bench_call_tracing_with_different_executor(c: &mut Criterion) { + c.bench_function("call_tracing_with_different_executor", |b| { + let source = include_str!("../tests/contracts/calls_trace.hex"); + let bytecode = hex::decode(source).unwrap(); + let fn_sig = "test_call_success_success_failed()"; + let fn_args_hex = ""; + let add_hex = format!("{}{}", fn_sig_to_prefix(fn_sig), fn_args_hex); + + let data = hex::decode(add_hex).unwrap(); + b.iter(|| { + let mut exe = TinyEVM::default(); + let resp = exe + .deploy_helper( + OWNER, + bytecode.clone(), + UZERO, + None, + Some(DEPLOY_TO_ADDRESS), + ) + .unwrap(); + + let address = Address::from_slice(&resp.data); + + let _ = exe.contract_call_helper(address, OWNER, data.clone(), UZERO, None); + }) + }); +} + +criterion_group!( + name = evm_benches; + config = Criterion::default(); + targets = bench_call_tracing_with_shared_executor, bench_call_tracing_with_different_executor +); + +criterion_main!(evm_benches); diff --git a/benches/infinite_loop.rs b/benches/infinite_loop.rs new file mode 100644 index 0000000..68d4f37 --- /dev/null +++ b/benches/infinite_loop.rs @@ -0,0 +1,67 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use primitive_types::U256; +use revm::primitives::Address; +use tinyevm::{fn_sig_to_prefix, TinyEVM, UZERO}; + +const OWNER: Address = Address::repeat_byte(0x01); +const DEPLOY_TO_ADDRESS: Address = Address::repeat_byte(0x02); + +// ~100ms per loop +fn bench_infinite_loop_math(c: &mut Criterion) { + c.bench_function("infinite_loop_with_simple_math", |b| { + let source = include_str!("../tests/contracts/infinite_loop_Test2.hex"); + let bytecode = hex::decode(source).unwrap(); + let fn_sig = "test1(int256)"; + let fn_args_hex = format!("{:0>64x}", U256::from(0)); + let add_hex = format!("{}{}", fn_sig_to_prefix(fn_sig), fn_args_hex); + + let data = hex::decode(add_hex).unwrap(); + let mut exe = TinyEVM::default(); + + let resp = { + exe.deploy_helper(OWNER, bytecode, UZERO, None, Some(DEPLOY_TO_ADDRESS)) + .unwrap() + }; + + assert!(resp.success, "Contract deploy should succeed."); + let address = Address::from_slice(&resp.data); + + b.iter(|| { + let _ = exe.contract_call_helper(address, OWNER, data.clone(), UZERO, None); + }) + }); +} + +// <300ms per loop +fn bench_infinite_loop_adderss_call(c: &mut Criterion) { + c.bench_function("infinite_loop_with_address_call", |b| { + let source = include_str!("../tests/contracts/infinite_loop_Test.hex"); + let bytecode = hex::decode(source).unwrap(); + let fn_sig = "test1(int256)"; + let fn_args_hex = format!("{:0>64x}", U256::from(0)); + let add_hex = format!("{}{}", fn_sig_to_prefix(fn_sig), fn_args_hex); + + let data = hex::decode(add_hex).unwrap(); + let mut exe = TinyEVM::default(); + + let resp = { + exe.deploy_helper(OWNER, bytecode, UZERO, None, Some(DEPLOY_TO_ADDRESS)) + .unwrap() + }; + + assert!(resp.success, "Contract deploy should succeed."); + let address = Address::from_slice(&resp.data); + + b.iter(|| { + let _ = exe.contract_call_helper(address, OWNER, data.clone(), UZERO, None); + }) + }); +} + +criterion_group!( + name = infinite_loop; + config = Criterion::default(); + targets = bench_infinite_loop_math, bench_infinite_loop_adderss_call +); + +criterion_main!(infinite_loop); diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..77928c2 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,8 @@ +pytest>=3.5.0 +pip>=21.3 +maturin>=0.12,<0.13 +pycryptodome>=3.10.1 +pytest-benchmark +eth_abi +maturin +memory_profiler diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/src/cache/mod.rs b/src/cache/mod.rs index 2c34de9..053d1f7 100644 --- a/src/cache/mod.rs +++ b/src/cache/mod.rs @@ -6,6 +6,11 @@ pub mod filesystem_cache; #[cfg(feature = "redis")] pub mod redis_cache; +#[cfg(not(feature = "redis"))] +pub use filesystem_cache::FileSystemProviderCache as DefaultProviderCache; +#[cfg(feature = "redis")] +pub use redis_cache::RedisProviderCache as DefaultProviderCache; + pub trait ProviderCache: Clone + Default { fn store( &self, diff --git a/src/chain_inspector.rs b/src/chain_inspector.rs new file mode 100644 index 0000000..5627a76 --- /dev/null +++ b/src/chain_inspector.rs @@ -0,0 +1,113 @@ +use revm::interpreter::{CallInputs, CallOutcome, CreateInputs, CreateOutcome}; +use revm::primitives::Log; +use revm::{interpreter::Interpreter, Database, EvmContext, Inspector}; + +use crate::instrument::bug_inspector::BugInspector; +use crate::instrument::log_inspector::LogInspector; + +/// A chain of inspectors, ecch inspector will be executed in order. +pub struct ChainInspector { + pub log_inspector: Option, + pub bug_inspector: Option, +} + +impl Inspector for ChainInspector { + #[inline] + fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { + if let Some(ins) = self.log_inspector.as_mut() { + ins.step(interp, context); + } + if let Some(ins) = self.bug_inspector.as_mut() { + ins.step(interp, context); + } + } + + #[inline] + fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { + if let Some(ins) = self.log_inspector.as_mut() { + ins.step_end(interp, context); + } + if let Some(ins) = self.bug_inspector.as_mut() { + ins.step_end(interp, context); + } + } + + #[inline] + fn log(&mut self, context: &mut EvmContext, log: &Log) { + if let Some(ins) = self.log_inspector.as_mut() { + ins.log(context, log); + } + if let Some(ins) = self.bug_inspector.as_mut() { + ins.log(context, log); + } + } + + /// Call the inspectors in order, if any of them returns a `Some`, return that value. + /// If all of them return `None`, the execution will continue normally. + #[inline] + fn call( + &mut self, + context: &mut EvmContext, + inputs: &mut CallInputs, + ) -> Option { + if let Some(ins) = self.log_inspector.as_mut() { + ins.call(context, inputs); + } + if let Some(ins) = self.bug_inspector.as_mut() { + ins.call(context, inputs) + } else { + None + } + } + + #[inline] + fn call_end( + &mut self, + context: &mut EvmContext, + inputs: &CallInputs, + outcome: CallOutcome, + ) -> CallOutcome { + let mut outcome = outcome; + if let Some(ins) = self.log_inspector.as_mut() { + outcome = ins.call_end(context, inputs, outcome); + } + if let Some(ins) = self.bug_inspector.as_mut() { + outcome = ins.call_end(context, inputs, outcome); + } + outcome + } + + /// Call the inspectors in order, if any of them returns a `Some`, return that value. + #[inline] + fn create( + &mut self, + context: &mut EvmContext, + inputs: &mut CreateInputs, + ) -> Option { + if let Some(ins) = self.log_inspector.as_mut() { + ins.create(context, inputs); + } + if let Some(ins) = self.bug_inspector.as_mut() { + ins.create(context, inputs) + } else { + None + } + } + + #[inline] + fn create_end( + &mut self, + context: &mut EvmContext, + inputs: &CreateInputs, + outcome: CreateOutcome, + ) -> CreateOutcome { + let mut outcome = outcome; + if let Some(ins) = self.log_inspector.as_mut() { + outcome = ins.create_end(context, inputs, outcome); + } + if let Some(ins) = self.bug_inspector.as_mut() { + outcome = ins.create_end(context, inputs, outcome); + } + outcome + } +} diff --git a/src/common.rs b/src/common.rs index c7a6b3a..0a6bf4b 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1,8 +1,11 @@ +use std::cmp::Ordering; + /// Common constants, data structures and functions to be used by both rust-evm and revm use eyre::Result; use hex::ToHex; use num_bigint::BigInt; use primitive_types::H256; +use revm::interpreter::instructions::i256::i256_cmp; use ruint::aliases::U256; use sha3::{Digest, Keccak256}; @@ -57,14 +60,20 @@ pub fn bigint_to_ruint_u256(b: &BigInt) -> Result { return Err(eyre::eyre!("BigInt is negative")); } - let mut padded_bytes = [0u8; 32]; let bytes_len = bytes.len(); if bytes_len > 32 { return Err(eyre::eyre!("BigInt is too large")); } - // Copy bytes into the end of the padded array - padded_bytes[(32 - bytes_len)..].copy_from_slice(&bytes); + Ok(U256::from_be_slice(&bytes)) +} - Ok(U256::from_be_slice(&padded_bytes)) +/// Returns the distance between two U256 numbers +#[inline(always)] +pub fn i256_diff(first: &U256, second: &U256) -> (U256, bool) { + match i256_cmp(first, second) { + Ordering::Equal => (U256::ZERO, false), + Ordering::Greater => first.overflowing_sub(*second), + Ordering::Less => second.overflowing_sub(*first), + } } diff --git a/src/fork_db.rs b/src/fork_db.rs index fad0bf9..9c0b572 100644 --- a/src/fork_db.rs +++ b/src/fork_db.rs @@ -1,14 +1,10 @@ -use hashbrown::hash_map::Entry; -use hashbrown::{HashMap, HashSet}; -use std::env; - -use crate::cache::filesystem_cache::FileSystemProviderCache; -use crate::cache::ProviderCache; +use crate::cache::{DefaultProviderCache, ProviderCache}; use crate::fork_provider::ForkProvider; -use crate::instrument::bug::{BugData, Heuristics, InstrumentConfig}; use crate::CALL_DEPTH; use ethers::types::{Block, TxHash}; use eyre::{ContextCompat, Result}; +use hashbrown::hash_map::Entry; +use hashbrown::{HashMap, HashSet}; use primitive_types::H256; use revm::db::{AccountState, DbAccount}; use revm::primitives::{ @@ -16,24 +12,9 @@ use revm::primitives::{ U256, }; use revm::{Database, DatabaseCommit}; +use std::env; use tracing::{debug, info, trace}; -#[derive(Debug, Clone, Default)] -pub struct InstrumentData { - pub bug_data: BugData, - pub heuristics: Heuristics, - // Mapping from contract address to a set of PCs seen in the execution - pub pcs_by_address: HashMap>, - // Holding the addresses created in the current transaction, - // must be cleared by transaction caller before or after each transaction - pub created_addresses: Vec
, - // Managed addresses: contract -> addresses created by any transaction from the contract - pub managed_addresses: HashMap>, - pub opcode_index: usize, - pub last_index_sub: usize, - pub last_index_eq: usize, -} - #[derive(Debug, Default)] pub struct ForkDB { /// Account info where None means it is not existing. Not existing state is needed for Pre TANGERINE forks. @@ -41,7 +22,7 @@ pub struct ForkDB { pub accounts: HashMap, /// Tracks all contracts by their code hash. pub contracts: HashMap, - /// All cached block hashes from the [DatabaseRef]. + /// All cached block hashes pub block_hashes: HashMap, pub fork_enabled: bool, @@ -57,14 +38,9 @@ pub struct ForkDB { block_cache: HashMap>, /// Max depth to consider when forking address max_fork_depth: usize, - - /// Optional instrument config - pub instrument_config: Option, - /// Instrument data collected - pub instrument_data: InstrumentData, } -impl Clone for ForkDB { +impl Clone for ForkDB { fn clone(&self) -> Self { Self { accounts: self.accounts.clone(), @@ -77,8 +53,6 @@ impl Clone for ForkDB { block_cache: self.block_cache.clone(), ignored_addresses: self.ignored_addresses.clone(), max_fork_depth: self.max_fork_depth, - instrument_config: self.instrument_config.clone(), - instrument_data: self.instrument_data.clone(), } } } @@ -156,8 +130,6 @@ impl ForkDB { block_cache: HashMap::new(), ignored_addresses: Default::default(), max_fork_depth, - instrument_config: Some(InstrumentConfig::default()), - instrument_data: InstrumentData::default(), } } diff --git a/src/instrument/bug.rs b/src/instrument/bug.rs index 9782f9b..ca44ceb 100644 --- a/src/instrument/bug.rs +++ b/src/instrument/bug.rs @@ -8,18 +8,23 @@ use strum_macros::Display; pub enum BugType { IntegerOverflow, IntegerSubUnderflow, - IntegerDivByZero, // op2 for DIV, SDIV is zero - IntegerModByZero, // op2 for MOD, SMOD, ADDMOD, MULMOD, is zero + /// op2 for DIV, SDIV is zero + IntegerDivByZero, + /// op2 for MOD, SMOD, ADDMOD, MULMOD, is zero + IntegerModByZero, PossibleIntegerTruncation, TimestampDependency, BlockNumberDependency, BlockValueDependency, TxOriginDependency, - Call(usize, H160), // Call(input_parameter_size, destination_address) + /// Call(input_parameter_size, destination_address) + Call(usize, H160), RevertOrInvalid, - Jumpi(usize), // Jumpi(dest) + /// Jumpi(dest) + Jumpi(usize), Sload(U256), - Sstore(U256, U256), // index, value + /// storage key, value + Sstore(U256, U256), Unclassified, } @@ -62,21 +67,46 @@ impl std::fmt::Display for Bug { #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] #[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))] pub struct MissedBranch { - /// Previous program counter - // pub prev_pc: usize, + // The pc imediately before the conditional jumpi pub prev_pc: usize, - /// Missed program counter - pub pc: usize, + /// Condition of the jumpi, true jump to `dest_pc`, false jump to `prev_pc + 1` + pub cond: bool, + /// Destination pc if condition is true + pub dest_pc: usize, /// Distiance required to reach the missed branch pub distance: U256, + /// Address of the contract in which this operation is executed + pub address_index: isize, +} + +impl MissedBranch { + pub fn new( + prev_pc: usize, + dest_pc: usize, + cond: bool, + distance: U256, + address_index: isize, + ) -> Self { + Self { + prev_pc, + dest_pc, + cond, + distance, + address_index, + } + } } -impl From<(usize, usize, U256)> for MissedBranch { - fn from((prev_pc, pc, distance): (usize, usize, U256)) -> Self { +impl From<(usize, usize, bool, U256, isize)> for MissedBranch { + fn from( + (prev_pc, dest_pc, cond, distance, address_index): (usize, usize, bool, U256, isize), + ) -> Self { Self { prev_pc, - pc, + dest_pc, + cond, distance, + address_index, } } } @@ -134,30 +164,28 @@ impl Heuristics { } /// Record missing branch data - pub fn record_missed_branch(&mut self, missed_pc: usize) { - let prev_pc = self.coverage.back(); - if prev_pc.is_none() { - return; - } - - let prev_pc = prev_pc.unwrap(); - let pc = missed_pc; + pub fn record_missed_branch( + &mut self, + prev_pc: usize, + dest_pc: usize, + cond: bool, + address_index: isize, + ) { let distance = self.distance; if self.missed_branches.iter_mut().any(|x| { - if x.pc == pc && x.prev_pc == *prev_pc { - if distance != x.distance { - x.distance = distance - } - true - } else { - false - } + matches!(x, MissedBranch { prev_pc: p, dest_pc: d, distance: dist, .. } if *p == prev_pc && *d == dest_pc && *dist == distance) }) { return; } - self.missed_branches.push((*prev_pc, pc, distance).into()); + self.missed_branches.push(MissedBranch::new( + prev_pc, + dest_pc, + cond, + distance, + address_index, + )); // if self.missed_branchs.len() > 2 { // self.missed_branchs.drain(0..self.missed_branchs.len() - 2); // } @@ -168,7 +196,7 @@ impl Heuristics { #[derive(Clone, Debug)] #[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))] pub struct InstrumentConfig { - /// Master switch to toggle instrumentation + /// Set true to enable bug_inspector pub enabled: bool, /// Enable recording seen PCs by current contract address pub pcs_by_address: bool, diff --git a/src/instrument/bug_inspector.rs b/src/instrument/bug_inspector.rs new file mode 100644 index 0000000..1994387 --- /dev/null +++ b/src/instrument/bug_inspector.rs @@ -0,0 +1,598 @@ +use hashbrown::{HashMap, HashSet}; +use primitive_types::{H160, H256}; +use revm::{ + interpreter::{CreateInputs, CreateOutcome, Interpreter, OpCode}, + primitives::{Address, U256}, + Database, EvmContext, Inspector, +}; +use tracing::{debug, warn}; + +use crate::i256_diff; + +use super::{Bug, BugData, BugType, Heuristics, InstrumentConfig}; + +#[derive(Default)] +pub struct BugInspector { + /// Change the created address to another address + pub create_address_overrides: HashMap, + pub bug_data: BugData, + pub heuristics: Heuristics, + // Mapping from contract address to a set of PCs seen in the execution + pub pcs_by_address: HashMap>, + pub instrument_config: InstrumentConfig, + // Holding the addresses created in the current transaction, + // must be cleared by transaction caller before or after each transaction + pub created_addresses: Vec
, + // Managed addresses: contract -> addresses created by any transaction from the contract + pub managed_addresses: HashMap>, + /// Stack inputs of the current opcodes. Only updated when the opcode is interesting + inputs: Vec, + /// Current opcode + opcode: Option, + // Current program counter + pc: usize, + /// Current index in the execution. For tracking peephole optimized if-statement + step_index: u64, + last_index_sub: u64, + last_index_eq: u64, +} + +impl BugInspector { + pub fn enabled(&self) -> bool { + self.instrument_config.enabled + } + + pub fn inc_step_index(&mut self) { + self.step_index += 1; + } + + /// Returns true if this is possible peephole optimized code, + /// assuming when calling this function the current opcode is + /// JUMPI + pub fn possibly_if_equal(&self) -> bool { + self.step_index < self.last_index_sub + 10 && self.step_index > self.last_index_eq + 10 + } + + fn record_seen_address(&mut self, address: Address) -> isize { + // make sure target_address is the first address added + if self.instrument_config.record_branch_for_target_only { + if self.heuristics.seen_addresses.is_empty() { + self.heuristics + .seen_addresses + .push(self.instrument_config.target_address); + } + + if self.instrument_config.target_address == address { + return 0; + } + } + + let idx = self + .heuristics + .seen_addresses + .iter() + .position(|a| *a == address); + if let Some(i) = idx { + return i as isize; + } + + self.heuristics.seen_addresses.push(address); + self.heuristics.seen_addresses.len() as isize - 1 + } + + /// Record the program counter for the given contract address + pub fn record_pc(&mut self, address: Address, pc: usize) { + let pcs = self.pcs_by_address.entry(address).or_default(); + pcs.insert(pc); + } + + pub fn add_bug(&mut self, bug: Bug) { + match bug.bug_type { + BugType::Jumpi(dest) => { + if self.instrument_config.heuristics { + // March 15 bug patch: keep last 256 elements + self.heuristics.coverage.push_back(dest); + if self.heuristics.coverage.len() > 256 { + self.heuristics.coverage.pop_front(); + } + } + } + BugType::Sload(_key) => { + if self.bug_data.len() > 256 { + // this will lead to poor performance + // self.bug_data.retain(|front| { + // !(front.address_index == address_idx + // && matches!(front.bug_type, BugType::Sload(k) if k == key)) + // }); + self.bug_data.pop_front(); + } + self.bug_data.push_back(bug); + } + BugType::Sstore(_key, _) => { + if self.bug_data.len() > 256 { + // self.bug_data.retain(|front| { + // !(front.address_index == address_idx + // && matches!(front.bug_type, BugType::Sstore(k, _) if k == key)) + // }); + self.bug_data.pop_front(); + } + self.bug_data.push_back(bug); + } + _ => self.bug_data.push_back(bug), + } + } +} + +impl Inspector for BugInspector +where + DB: Database, +{ + #[inline] + fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { + if !self.enabled() { + return; + } + + let _ = interp; + let _ = context; + let opcode = interp.current_opcode(); + let opcode = OpCode::new(opcode); + self.opcode = opcode; + self.pc = interp.program_counter(); + + if let Some(OpCode::EQ) = opcode { + self.last_index_eq = self.step_index; + } + + if let Some(OpCode::SUB) = opcode { + self.last_index_sub = self.step_index; + } + + self.inputs.clear(); + if let Some( + op @ (OpCode::JUMPI + | OpCode::CALL + | OpCode::CALLCODE + | OpCode::DELEGATECALL + | OpCode::STATICCALL + | OpCode::SSTORE + | OpCode::SLOAD + | OpCode::ADD + | OpCode::SUB + | OpCode::MUL + | OpCode::DIV + | OpCode::SDIV + | OpCode::MOD + | OpCode::SMOD + | OpCode::EXP + | OpCode::LT + | OpCode::SLT + | OpCode::GT + | OpCode::SGT + | OpCode::EQ + | OpCode::AND + | OpCode::ADDMOD + | OpCode::MULMOD + | OpCode::KECCAK256), + ) = opcode + { + let num_inputs = op.inputs(); + for i in 0..num_inputs { + if let Ok(v) = interp.stack().peek(i as usize) { + self.inputs.push(v); + } else { + break; + } + } + } + + self.inc_step_index(); + } + + #[inline] + fn step_end(&mut self, interp: &mut Interpreter, _context: &mut EvmContext) { + if !self.enabled() { + return; + } + let address = interp.contract().target_address; + let address_index = self.record_seen_address(address); + let opcode = self.opcode; + let pc = self.pc; + + if self.instrument_config.pcs_by_address { + self.record_pc(address, pc); + } + + match opcode { + Some(op @ OpCode::ADD) => { + if let Ok(r) = interp.stack().peek(0) { + if let (Some(a), Some(b)) = (self.inputs.first(), self.inputs.get(1)) { + if r < *a || r < *b { + let bug = + Bug::new(BugType::IntegerOverflow, op.get(), pc, address_index); + self.add_bug(bug); + } + } + } + } + Some(op @ OpCode::MUL) => { + if let (Some(a), Some(b)) = (self.inputs.first(), self.inputs.get(1)) { + if mul_overflow(*a, *b) { + let bug = Bug::new(BugType::IntegerOverflow, op.get(), pc, address_index); + self.add_bug(bug); + } + } + } + Some(op @ OpCode::SUB) => { + if let (Some(a), Some(b)) = (self.inputs.first(), self.inputs.get(1)) { + if a < b { + let bug = + Bug::new(BugType::IntegerSubUnderflow, op.get(), pc, address_index); + self.add_bug(bug); + } + } + } + Some(op @ (OpCode::SMOD | OpCode::MOD)) => { + if let Some(b) = self.inputs.get(1) { + if *b == U256::ZERO { + let bug = Bug::new(BugType::IntegerModByZero, op.get(), pc, address_index); + self.add_bug(bug); + } + } + } + Some(op @ (OpCode::DIV | OpCode::SDIV)) => { + if let Some(b) = self.inputs.get(1) { + if *b == U256::ZERO { + let bug = Bug::new(BugType::IntegerDivByZero, op.get(), pc, address_index); + self.add_bug(bug); + } + } + } + Some(op @ (OpCode::ADDMOD | OpCode::MULMOD)) => { + if let Some(n) = self.inputs.get(2) { + if n == &U256::ZERO { + let bug = Bug::new(BugType::IntegerModByZero, op.get(), pc, address_index); + self.add_bug(bug); + } + } + } + Some(op @ OpCode::EXP) => { + // todo_cl check for overflow + if let (Some(a), Some(b), Ok(r)) = ( + self.inputs.first(), + self.inputs.get(1), + interp.stack().peek(0), + ) { + if exp_overflow(*a, *b, r) { + let bug = Bug::new(BugType::IntegerOverflow, op.get(), pc, address_index); + self.add_bug(bug); + } + } + } + Some(OpCode::LT) => { + if let (Some(a), Some(b)) = (self.inputs.first(), self.inputs.get(1)) { + let distance = if a >= b { + a.overflowing_sub(*b).0.saturating_add(U256::from(1)) + } else { + b.overflowing_sub(*a).0 + }; + self.heuristics.distance = distance; + } + } + Some(OpCode::GT) => { + if let (Some(a), Some(b)) = (self.inputs.first(), self.inputs.get(1)) { + let distance = if a >= b { + a.overflowing_sub(*b).0 + } else { + b.overflowing_sub(*a).0.saturating_add(U256::from(1)) + }; + self.heuristics.distance = distance; + } + } + Some(OpCode::SLT) => { + if let (Some(a), Some(b), Ok(r)) = ( + self.inputs.first(), + self.inputs.get(1), + interp.stack().peek(0), + ) { + let mut distance = if a >= b { + a.overflowing_sub(*b).0 + } else { + b.overflowing_sub(*a).0 + }; + if r == U256::ZERO { + distance = distance.saturating_add(U256::from(1)); + } + self.heuristics.distance = distance; + } + } + Some(OpCode::SGT) => { + if let (Some(a), Some(b), Ok(r)) = ( + self.inputs.first(), + self.inputs.get(1), + interp.stack().peek(0), + ) { + let (mut distance, _) = i256_diff(a, b); + if r == U256::ZERO { + distance = distance.saturating_add(U256::from(1)); + } + self.heuristics.distance = distance; + } + } + Some(OpCode::EQ) => { + if let (Some(a), Some(b), Ok(r)) = ( + self.inputs.first(), + self.inputs.get(1), + interp.stack().peek(0), + ) { + let mut distance = if a > b { + a.overflowing_sub(*b).0 + } else { + b.overflowing_sub(*a).0 + }; + if r != U256::ZERO { + distance = U256::from(1); + } + self.heuristics.distance = distance; + } + } + Some(op @ OpCode::AND) => { + if let (Some(a), Some(b)) = (self.inputs.first(), self.inputs.get(1)) { + // check if there is an possible truncation + + // For AND operator, if either side of the operands equals + // u8, u16, ..., and the other side is larger than this + // operand, generate possible integer truncation signal + let mut i = 1; + let possible_overflow = loop { + if i == 32 { + break false; + } + + let r = U256::MAX >> (i * 8); + + if r == *a && b > a { + break true; + } + + if r == *b && a > b { + break true; + } + i += 1; + }; + if possible_overflow { + let bug = Bug::new( + BugType::PossibleIntegerTruncation, + op.get(), + pc, + address_index, + ); + self.add_bug(bug); + } + } + } + Some(op @ OpCode::SSTORE) => { + if let (Some(key), Some(value)) = (self.inputs.first(), self.inputs.get(1)) { + let bug = Bug::new( + BugType::Sstore(*key, *value), + op.get(), + self.pc, + address_index, + ); + self.add_bug(bug); + } + } + Some(op @ OpCode::SLOAD) => { + if let Some(key) = self.inputs.first() { + let bug = Bug::new(BugType::Sload(*key), op.get(), self.pc, address_index); + self.add_bug(bug); + } + } + Some(op @ OpCode::ORIGIN) => { + let bug = Bug::new( + BugType::TxOriginDependency, + op.get(), + self.pc, + address_index, + ); + self.add_bug(bug); + } + + Some( + op @ (OpCode::CALL | OpCode::CALLCODE | OpCode::DELEGATECALL | OpCode::STATICCALL), + ) => { + let in_len = { + if matches!(op, OpCode::CALL | OpCode::CALLCODE) { + self.inputs.get(4) + } else { + self.inputs.get(3) + } + }; + let address = self.inputs.get(1); + + if let (Some(in_len), Some(callee)) = (in_len, address) { + let callee_bytes: [u8; 32] = callee.to_be_bytes(); + let callee = H160::from_slice(&callee_bytes[12..]); + let in_len = usize::try_from(in_len).unwrap(); + let bug = Bug::new( + BugType::Call(in_len, callee), + op.get(), + self.pc, + address_index, + ); + self.add_bug(bug); + } + } + Some(op @ OpCode::JUMPI) => { + // Check for missed branches + let target_address = self.instrument_config.target_address; + macro_rules! update_heuritics { + // (prev_pc, dest_pc_if_cond_is_true, cond) + ($prev_pc: ident, $dest_pc: expr, $cond: expr) => { + if !self.instrument_config.record_branch_for_target_only + || address == target_address + { + let heuristics = &mut self.heuristics; + heuristics.record_missed_branch( + $prev_pc, + $dest_pc, + $cond, + address_index, + ); + let target = if $cond { $dest_pc } else { $prev_pc + 1 }; + let bug = + Bug::new(BugType::Jumpi(target), op.get(), $prev_pc, address_index); + self.add_bug(bug); + } + }; + } + + // NOTE: invalid jumps are ignored + if let (Some(counter), Some(cond)) = (self.inputs.first(), self.inputs.get(1)) { + // Check for distance in peephole optimized if-statement + if self.possibly_if_equal() { + let max = U256::MAX; + let mut half = U256::MAX; + half.set_bit(31, false); + let h = &mut self.heuristics; + h.distance = { + // smallest distance from the `value` to U256::MAX and 0 + if *cond > half { + max - cond + U256::from(1) + } else { + *cond + } + }; + } + + let dest = usize::try_from(counter).unwrap(); + let cond = *cond != U256::ZERO; + update_heuritics!(pc, dest, cond); + } + } + Some(op @ OpCode::BLOBHASH) => { + let bug = Bug::new(BugType::BlockValueDependency, op.get(), pc, address_index); + self.add_bug(bug); + } + Some(op @ OpCode::COINBASE) => { + let bug = Bug::new(BugType::BlockValueDependency, op.get(), pc, address_index); + self.add_bug(bug); + } + Some(op @ OpCode::TIMESTAMP) => { + let bug = Bug::new(BugType::TimestampDependency, op.get(), pc, address_index); + self.add_bug(bug); + } + Some(op @ OpCode::NUMBER) => { + let bug = Bug::new(BugType::BlockNumberDependency, op.get(), pc, address_index); + self.add_bug(bug); + } + Some(op @ OpCode::DIFFICULTY) => { + let bug = Bug::new(BugType::BlockValueDependency, op.get(), pc, address_index); + self.add_bug(bug); + } + Some(op @ (OpCode::REVERT | OpCode::INVALID)) => { + let bug = Bug::new(BugType::RevertOrInvalid, op.get(), pc, address_index); + self.add_bug(bug); + } + Some(op @ (OpCode::SELFDESTRUCT | OpCode::CREATE | OpCode::CREATE2)) => { + let bug = Bug::new(BugType::Unclassified, op.get(), pc, address_index); + self.add_bug(bug); + if matches!(op, OpCode::CREATE | OpCode::CREATE2) { + if let Ok(created_address) = interp.stack.peek(0) { + let bytes: [u8; 32] = created_address.to_be_bytes(); + let created_address = Address::from_slice(&bytes[12..]); + self.record_seen_address(created_address); + } + } + } + Some(OpCode::KECCAK256) => { + if self.instrument_config.record_sha3_mapping { + if let (Some(offset), Some(size), Ok(output)) = ( + self.inputs.first(), + self.inputs.get(1), + interp.stack().peek(0), + ) { + let offset = offset.as_limbs()[0] as usize; + let size = size.as_limbs()[0] as usize; + let input = &interp.shared_memory.context_memory()[offset..offset + size]; + // get only last 32 bytes + let last_32 = { + if input.len() > 32 { + &input[input.len() - 32..] + } else { + input + } + }; + let output = H256::from_slice(&output.to_be_bytes::<32>()); + self.heuristics.record_sha3_mapping(last_32, output); + } + } + } + _ => (), + } + } + + #[inline] + fn create_end( + &mut self, + context: &mut EvmContext, + _inputs: &CreateInputs, + outcome: CreateOutcome, + ) -> CreateOutcome { + if !self.enabled() { + return outcome; + } + + let CreateOutcome { result, address } = &outcome; + if let Some(address) = address { + if let Some(override_address) = self.create_address_overrides.get(address) { + debug!( + "Overriding created address {:?} with {:?}", + address, override_address + ); + let state = &mut context.journaled_state.state; + if let Some(value) = state.remove(address) { + state.insert(*override_address, value); + } else { + warn!( + "Contract created but no state associated with it? Contract address: {:?}", + address + ); + } + + return CreateOutcome::new(result.to_owned(), Some(*override_address)); + } + } + outcome + } +} + +fn mul_overflow(a: U256, b: U256) -> bool { + let zero = U256::ZERO; + if a == zero || b == zero { + false + } else { + a > U256::MAX.wrapping_div(b) + } +} + +fn exp_overflow(a: U256, b: U256, r: U256) -> bool { + let max_value: U256 = U256::MAX; + let mut result: U256 = U256::from(1u64); + + if b == U256::ZERO { + return r != U256::from(1u64); + } + + let mut i = U256::ZERO; + + while i < b { + if result > max_value / a { + return true; + } + result *= a; + i += U256::from(1u64); + } + + result != r +} diff --git a/src/logs.rs b/src/instrument/log_inspector.rs similarity index 77% rename from src/logs.rs rename to src/instrument/log_inspector.rs index 13b5a01..d9784a8 100644 --- a/src/logs.rs +++ b/src/instrument/log_inspector.rs @@ -1,11 +1,7 @@ use crate::CALL_DEPTH; -use hashbrown::HashMap; use lazy_static::lazy_static; use revm::{ - interpreter::{ - CallInputs, CallOutcome, CallScheme, CallValue, CreateInputs, CreateOutcome, - InstructionResult, - }, + interpreter::{CallInputs, CallOutcome, CallScheme, CallValue, InstructionResult}, primitives::{Address, Bytes, Log as EvmLog, B256, U256}, Database, EvmContext, Inspector, }; @@ -39,22 +35,21 @@ pub struct Log { } /// An inspector that collects call traces. -#[derive(Debug)] -pub struct LogsInspector<'a> { +#[derive(Debug, Default)] +pub struct LogInspector { /// Traced enabled? pub trace_enabled: bool, /// The collected traces - pub traces: &'a mut Vec, + pub traces: Vec, /// EVM events/logs collected during execution - pub logs: &'a mut Vec, - /// Overrides for addresses. Any address created in this map keys will be overriden with the value - pub override_addresses: &'a HashMap, + pub logs: Vec, } -impl Inspector for LogsInspector<'_> +impl Inspector for LogInspector where DB: Database, { + #[inline] fn log(&mut self, _context: &mut EvmContext, evm_log: &EvmLog) { if !self.trace_enabled { return; @@ -72,6 +67,7 @@ where }); } + #[inline] fn call( &mut self, _context: &mut EvmContext, @@ -116,6 +112,7 @@ where None } + #[inline] fn call_end( &mut self, _context: &mut EvmContext, @@ -137,19 +134,4 @@ where result } - - fn create_end( - &mut self, - _context: &mut EvmContext, - _inputs: &CreateInputs, - outcome: CreateOutcome, - ) -> CreateOutcome { - let CreateOutcome { result, address } = outcome; - if let Some(address) = address { - if let Some(override_address) = self.override_addresses.get(&address) { - return CreateOutcome::new(result, Some(*override_address)); - } - } - CreateOutcome::new(result, address) - } } diff --git a/src/instrument/mod.rs b/src/instrument/mod.rs index ec7b02c..7103a22 100644 --- a/src/instrument/mod.rs +++ b/src/instrument/mod.rs @@ -1,2 +1,4 @@ pub mod bug; pub use bug::*; +pub mod bug_inspector; +pub mod log_inspector; diff --git a/src/lib.rs b/src/lib.rs index 7399e00..46e0a3d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -use crate::{fork_provider::ForkProvider, logs::LogsInspector, response::RevmResult}; +use crate::{fork_provider::ForkProvider, response::RevmResult}; use ::revm::{ db::DbAccount, primitives::{ @@ -7,25 +7,28 @@ use ::revm::{ }, Evm, }; -#[cfg(feature = "redis")] -use cache::redis_cache::RedisProviderCache as DefaultProviderCache; -use hashbrown::{HashMap, HashSet}; - -#[cfg(not(feature = "redis"))] -use cache::filesystem_cache::FileSystemProviderCache as DefaultProviderCache; - +use cache::DefaultProviderCache; +use chain_inspector::ChainInspector; use dotenv::dotenv; use ethers_providers::{Http, Provider}; use eyre::{eyre, ContextCompat, Result}; -use fork_db::{ForkDB, InstrumentData}; +use fork_db::ForkDB; +use hashbrown::{HashMap, HashSet}; use lazy_static::lazy_static; use num_bigint::BigInt; use pyo3::prelude::*; use response::{Response, SeenPcsMap, WrappedBug, WrappedHeuristics, WrappedMissedBranch}; +use revm::{ + inspector_handle_register, + primitives::{TxEnv, B256}, +}; use thread_local::ThreadLocal; use tokio::runtime::Runtime; +use uuid::Uuid; + /// Caching for Web3 provider mod cache; +mod chain_inspector; /// Common functions shared by both EVMs mod common; @@ -36,15 +39,15 @@ pub mod fork_db; /// Cache for the fork requests pub mod fork_provider; pub mod instrument; -/// Logging -mod logs; /// Provide response data structure from EVM pub mod response; pub use common::*; -use hex::ToHex; -use instrument::{BugData, Heuristics, InstrumentConfig}; +use hex::{FromHex, ToHex}; +use instrument::{ + bug_inspector::BugInspector, log_inspector::LogInspector, BugData, Heuristics, InstrumentConfig, +}; use ruint::aliases::U256; -use std::{cell::Cell, env, str::FromStr}; +use std::{cell::Cell, mem::replace, str::FromStr}; use tracing::{debug, info, trace}; lazy_static! { @@ -84,17 +87,22 @@ pub struct TinyEvmContext {} #[pyclass(unsendable)] pub struct TinyEVM { /// REVM instance - pub exe: Option>, + pub exe: Option>, pub owner: Address, + /// Default gas limit for each transaction + #[pyo3(get, set)] + tx_gas_limit: u64, /// Snapshots of account state pub snapshots: HashMap, /// Optional fork url pub fork_url: Option, + /// Snapshot of global states + global_snapshot: HashMap>, } static mut TRACE_ENABLED: bool = false; -/// Enable global tracing +/// Enable printing of trace logs for debugging #[pyfunction] pub fn enable_tracing() -> Result<()> { use tracing_subscriber::{fmt, EnvFilter}; @@ -118,29 +126,88 @@ pub fn enable_tracing() -> Result<()> { // Implementations for use in Rust impl TinyEVM { - pub fn instrument_data(&self) -> &InstrumentData { - &self.exe.as_ref().unwrap().context.evm.db.instrument_data + pub fn exe_mut(&mut self) -> &mut Evm<'static, ChainInspector, TinyEvmDb> { + self.exe.as_mut().unwrap() + } + + pub fn tx_mut(&mut self) -> &mut TxEnv { + self.exe_mut().tx_mut() + } + + fn db(&self) -> &ForkDB { + &self.exe.as_ref().unwrap().context.evm.db + } + + fn db_mut(&mut self) -> &mut ForkDB { + &mut self.exe.as_mut().unwrap().context.evm.db + } + + pub fn instrument_config_mut(&mut self) -> &mut InstrumentConfig { + &mut self.bug_inspector_mut().instrument_config + } + fn log_inspector(&self) -> &LogInspector { + self.exe + .as_ref() + .unwrap() + .context + .external + .log_inspector + .as_ref() + .unwrap() + } + + fn log_inspector_mut(&mut self) -> &mut LogInspector { + self.exe + .as_mut() + .unwrap() + .context + .external + .log_inspector + .as_mut() + .unwrap() + } + + fn bug_inspector(&self) -> &BugInspector { + self.exe + .as_ref() + .unwrap() + .context + .external + .bug_inspector + .as_ref() + .unwrap() + } + + fn bug_inspector_mut(&mut self) -> &mut BugInspector { + self.exe + .as_mut() + .unwrap() + .context + .external + .bug_inspector + .as_mut() + .unwrap() } pub fn bug_data(&self) -> &BugData { - &self.instrument_data().bug_data + &self.bug_inspector().bug_data } pub fn heuristics(&self) -> &Heuristics { - &self.instrument_data().heuristics + &self.bug_inspector().heuristics } pub fn pcs_by_address(&self) -> &HashMap> { - &self.instrument_data().pcs_by_address + &self.bug_inspector().pcs_by_address } pub fn created_addresses(&self) -> &Vec
{ - &self.instrument_data().created_addresses + &self.bug_inspector().created_addresses } /// Create a new TinyEVM instance without fork pub fn new_offline() -> Result { - Self::new(None, None) + Self::new_instance(None, None, false) } /// Set account balance, if the account does not exist, will create one @@ -183,7 +250,7 @@ impl TinyEVM { let db = &mut self.exe.as_mut().unwrap().context.evm.db; db.accounts.remove(&addr); - let managed_addresses = &mut db.instrument_data.managed_addresses; + let managed_addresses = &mut self.bug_inspector_mut().managed_addresses; managed_addresses.remove(&addr); Ok(()) @@ -195,7 +262,6 @@ impl TinyEVM { owner: Address, contract_bytecode: Vec, value: U256, - _overwrite: bool, // not supported yet tx_gas_limit: Option, force_address: Option
, // not supported yet ) -> Result { @@ -211,57 +277,38 @@ impl TinyEVM { // Reset instrumentation, self.clear_instrumentation(); - let db = &mut self.exe.as_mut().unwrap().context.evm.db; + self.bug_inspector_mut().pcs_by_address.clear(); // If don't want to trace the deploy PCs - db.instrument_data.pcs_by_address.clear(); // If don't want to trace the deploy PCs - - if let Some(exe) = self.exe.take() { - let exe = exe - .modify() - .modify_tx_env(|tx| { - tx.caller = owner; - tx.transact_to = TransactTo::Create; - tx.data = contract_bytecode.clone().into(); - tx.value = value; - tx.gas_limit = tx_gas_limit.unwrap_or(TX_GAS_LIMIT); - }) - .modify_cfg_env(|env| env.limit_contract_code_size = Some(0x24000)) - .build(); - self.exe = Some(exe); + { + let tx = self.exe.as_mut().unwrap().tx_mut(); + tx.caller = owner; + tx.transact_to = TransactTo::Create; + tx.data = contract_bytecode.clone().into(); + tx.value = value; + tx.gas_limit = tx_gas_limit.unwrap_or(self.tx_gas_limit); } - let nonce = self.exe.as_ref().unwrap().tx().nonce.unwrap_or_default(); + // todo this is read from global state, might be wrong + let nonce = self + .exe + .as_ref() + .unwrap() + .context + .evm + .db + .accounts + .get(&owner) + .map_or(0, |a| a.info.nonce); let address = owner.create(nonce); debug!("Calculated addresss: {:?}", address); - let mut traces = vec![]; - let mut logs = vec![]; - let mut override_addresses = HashMap::with_capacity(1); - let trace_enabled = matches!(env::var("TINYEVM_CALL_TRACE_ENABLED"), Ok(val) if val == "1"); - - let inspector = LogsInspector { - trace_enabled, - traces: &mut traces, - logs: &mut logs, - override_addresses: &override_addresses, - }; - - // todo add the inspector to the exe - - let result = self.exe.as_mut().unwrap().transact_commit(); - if let Some(force_address) = force_address { - override_addresses.insert(address, force_address); - self.clone_account(address, force_address, true)?; + self.bug_inspector_mut() + .create_address_overrides + .insert(address, force_address); } - - // debug!("db {:?}", self.exe.as_ref().unwrap().db()); - // debug!("sender {:?}", owner.encode_hex::(),); - - // // todo_cl temp check - // self.db = self.exe.as_ref().unwrap().db().clone(); - // self.env = self.exe.as_ref().unwrap().context.evm.env.as_ref().clone(); + let result = self.exe.as_mut().unwrap().transact_commit(); trace!("deploy result: {:?}", result); @@ -280,11 +327,6 @@ impl TinyEVM { }; if collision { - info!( - "Found address collision, reset the existing account: {}", - address.encode_hex::() - ); - return Err(eyre!( "Address collision for {}", address.encode_hex::() @@ -300,17 +342,14 @@ impl TinyEVM { addresses, address ); if !addresses.is_empty() { - self.exe - .as_mut() - .unwrap() - .context - .evm - .db - .instrument_data + self.bug_inspector_mut() .managed_addresses .insert(address, addresses); } + let logs = self.log_inspector().logs.clone(); + let traces = self.log_inspector().traces.clone(); + trace!("deploy result: {:?}", result); let revm_result = RevmResult { @@ -318,15 +357,12 @@ impl TinyEVM { bug_data, heuristics, seen_pcs, - transient_logs: vec![], - traces: vec![], + traces, + transient_logs: logs, ignored_addresses: Default::default(), }; - let mut resp: Response = revm_result.into(); - if let Some(force_address) = force_address { - resp.data = force_address.0.to_vec(); - } - Ok(resp) + + Ok(revm_result.into()) } /// Send a `transact_call` to a `contract` from the `sender` with raw @@ -343,42 +379,18 @@ impl TinyEVM { self.clear_instrumentation(); CALL_DEPTH.get_or_default().set(0); - debug!("db in contract_call: {:?}", self.exe.as_ref().unwrap().db()); - debug!("sender {:?}", sender.encode_hex::(),); - - if let Some(exe) = self.exe.take() { - let exe = exe - .modify() - .modify_tx_env(|tx| { - tx.caller = sender; - tx.transact_to = TransactTo::Call(contract); - tx.data = data.into(); - tx.value = value; - tx.gas_limit = tx_gas_limit.unwrap_or(TX_GAS_LIMIT); - }) - .build(); - self.exe = Some(exe); + { + let tx_gas_limit = tx_gas_limit.unwrap_or(self.tx_gas_limit); + let tx = self.tx_mut(); + tx.caller = sender; + tx.transact_to = TransactTo::Call(contract); + tx.data = data.into(); + tx.value = value; + tx.gas_limit = tx_gas_limit; } - let mut traces = vec![]; - let mut logs = vec![]; - let trace_enabled = matches!(env::var("TINYEVM_CALL_TRACE_ENABLED"), Ok(val) if val == "1"); - - let inspector = LogsInspector { - trace_enabled, - traces: &mut traces, - logs: &mut logs, - override_addresses: &mut Default::default(), - }; - - let result = { - let exe = self.exe.as_mut().unwrap(); - exe.transact_commit() - }; + let result = self.exe_mut().transact_commit(); - let bug_data = self.bug_data().clone(); - let heuristics = self.heuristics().clone(); - let seen_pcs = self.pcs_by_address().clone(); let addresses = self.created_addresses().clone(); info!( "created addresses from contract call: {:?} for {:?}", @@ -388,34 +400,38 @@ impl TinyEVM { debug!("contract_call result: {:?}", result); if !addresses.is_empty() { - let exe = self.exe.as_mut().unwrap(); - exe.context - .evm - .db - .instrument_data + self.bug_inspector_mut() .managed_addresses .insert(contract, addresses); } - let db = &self.exe.as_ref().unwrap().context.evm.db; + let bug_data = self.bug_data().clone(); + let heuristics = self.heuristics().clone(); + let seen_pcs = self.pcs_by_address().clone(); + + let db = &self.db(); let ignored_addresses = db.ignored_addresses.clone(); let ignored_addresses = ignored_addresses.into_iter().map(Into::into).collect(); + let log_inspector = self.log_inspector(); + let logs = log_inspector.logs.clone(); + let traces = log_inspector.traces.clone(); + let revm_result = RevmResult { result: result.map_err(|e| eyre!(e)), bug_data, heuristics, seen_pcs, - transient_logs: logs, traces, + transient_logs: logs, ignored_addresses, }; - revm_result.into() + Response::from(revm_result) } /// Set code of an account pub fn set_code_by_address(&mut self, addr: Address, code: Vec) -> Result<()> { - let db = &mut self.exe.as_mut().unwrap().context.evm.db; + let db = &mut self.db_mut(); let code = Bytecode::new_raw(code.into()); let accounts = &db.accounts; @@ -451,7 +467,7 @@ impl TinyEVM { /// Get code from an address pub fn get_code_by_address(&self, addr: Address) -> Result> { - let db = &self.exe.as_ref().unwrap().context.evm.db; + let db = &self.db(); let accounts = &db.accounts; let account = accounts.get(&addr); if let Some(account) = account { @@ -466,7 +482,7 @@ impl TinyEVM { /// Get Eth balance for an account pub fn get_eth_balance(&self, addr: Address) -> Result { - let db = &self.exe.as_ref().unwrap().context.evm.db; + let db = &self.db(); let accounts = &db.accounts; if let Some(account) = accounts.get(&addr) { Ok(account.info.balance) @@ -477,7 +493,7 @@ impl TinyEVM { /// Get storage by address and index pub fn get_storage_by_address(&self, addr: Address, index: U256) -> Result { - let db = &self.exe.as_ref().unwrap().context.evm.db; + let db = &self.db(); let accounts = &db.accounts; let account = accounts .get(&addr) @@ -495,7 +511,7 @@ impl TinyEVM { index: U256, value: U256, ) -> Result<()> { - let db = &mut self.exe.as_mut().unwrap().context.evm.db; + let db = self.db_mut(); db.insert_account_storage(addr, index, value)?; Ok(()) } @@ -515,21 +531,12 @@ impl TinyEVM { Ok(()) } -} - -impl Default for TinyEVM { - fn default() -> Self { - Self::new(None, None).unwrap() - } -} -// Implementations for use in Python and Rust -#[pymethods] -impl TinyEVM { - /// Create a new TinyEVM instance - #[new] - #[pyo3(signature = (fork_url = None, block_id = None))] - pub fn new(fork_url: Option, block_id: Option) -> Result { + pub fn new_instance( + fork_url: Option, + block_id: Option, + enable_call_trace: bool, // Whether to show call and event traces + ) -> Result { dotenv().ok(); let owner = Address::default(); @@ -541,7 +548,6 @@ impl TinyEVM { let fork_enabled = fork_url.is_some(); - // let mut db = InMemoryDB::default(); let mut db = match fork_url { Some(ref url) => { info!("Starting EVM from fork {} and block: {:?}", url, block_id); @@ -585,20 +591,53 @@ impl TinyEVM { }; db.insert_account_info(owner, account); + // let mut builder = Evm::builder(); + let log_inspector = LogInspector { + trace_enabled: enable_call_trace, + ..LogInspector::default() + }; + + let bug_inspector = BugInspector::default(); + + let inspector = ChainInspector { + log_inspector: Some(log_inspector), + bug_inspector: Some(bug_inspector), + }; let exe = Evm::builder() .modify_env(|e| *e = Box::new(env.clone())) .with_db(db.clone()) + .with_external_context(inspector) + .append_handler_register(inspector_handle_register) .build(); let tinyevm = Self { exe: Some(exe), owner, fork_url, + tx_gas_limit: TX_GAS_LIMIT, snapshots: HashMap::with_capacity(32), + global_snapshot: Default::default(), }; Ok(tinyevm) } +} + +impl Default for TinyEVM { + fn default() -> Self { + Self::new_instance(None, None, false).unwrap() + } +} + +// Implementations for use in Python and Rust +#[pymethods] +impl TinyEVM { + /// Create a new TinyEVM instance + #[new] + #[pyo3(signature = (fork_url = None, block_id = None))] + pub fn new(fork_url: Option, block_id: Option) -> Result { + Self::new_instance(fork_url, block_id, false) + } /// Get addresses loaded remotely as string pub fn get_forked_addresses(&self) -> Result> { @@ -618,9 +657,15 @@ impl TinyEVM { } /// Toggle for enable mode, only makes sense when fork_url is set - pub fn toggle_enable_fork(&mut self, enable: bool) { + pub fn toggle_enable_fork(&mut self, enabled: bool) { let db = &mut self.exe.as_mut().unwrap().context.evm.db; - db.fork_enabled = enable; + db.fork_enabled = enabled; + } + + /// Set whether to log the traces of the EVM execution + pub fn set_evm_tracing(&mut self, enabled: bool) { + let log_inspector = self.log_inspector_mut(); + log_inspector.trace_enabled = enabled; } /// Get the current fork toggle status @@ -647,7 +692,6 @@ impl TinyEVM { owner, hex::decode(contract_deploy_code)?, U256::default(), - true, None, None, ) @@ -659,27 +703,28 @@ impl TinyEVM { /// /// For optional arguments, you can use the empty string as inputs to use the default values. /// - /// [Source: ] - /// /// - `contract_deploy_code`: contract deploy binary array encoded as hex string - /// - `deploy_to_address`: Deploy the contract to the address /// - `owner`: Owner address as a 20-byte array encoded as hex string /// - `data`: (Optional, default empty) Constructor arguments encoded as hex string. /// - `value`: (Optional, default 0) a U256. Set the value to be included in the contract creation transaction. + /// - `deploy_to_address`: when provided, change the address of the deployed contract to this address, otherwise deploy to a an address created using `owner.CREATE2(a_fixed_salt, codehash)`. + /// - This requires the constructor to be payable. /// - The transaction sender (owner) must have enough balance /// - `init_value`: (Optional) BigInt. Override the initial balance of the contract to this value. /// /// Returns a list consisting of 4 items `[reason, address-as-byte-array, bug_data, heuristics]` - #[pyo3(signature = (contract_deploy_code, deploy_to_address, owner=None, data=None, value=None, init_value=None))] + #[pyo3(signature = (contract_deploy_code, salt=None, owner=None, data=None, value=None, init_value=None, deploy_to_address=None))] + #[allow(clippy::too_many_arguments)] pub fn deterministic_deploy( &mut self, contract_deploy_code: String, // variable length - deploy_to_address: String, + salt: Option, // h256 as hex string, has no effect if deploy_to_address is provided owner: Option, // h160 as hex string - data: Option, // variable length + data: Option, // variable length value: Option, init_value: Option, + deploy_to_address: Option, ) -> Result { let owner = { if let Some(owner) = owner { @@ -702,14 +747,30 @@ impl TinyEVM { let mut contract_bytecode = contract_deploy_code.to_vec(); contract_bytecode.extend(data); + let salt = { + if let Some(salt) = salt { + let salt = &salt; + B256::from_hex(salt)? + } else { + B256::ZERO + } + }; + + let force_address: Address = deploy_to_address + .map(|s| Address::from_str(&s)) + .transpose()? + .unwrap_or_else(|| { + let codehash = keccak256(&contract_bytecode); + owner.create2(salt, codehash) + }); + let resp = { let resp = self.deploy_helper( owner, contract_bytecode, bigint_to_ruint_u256(&value)?, - true, None, - Some(Address::from_str(&deploy_to_address)?), + Some(force_address), )?; if let Some(balance) = init_value { @@ -847,7 +908,7 @@ impl TinyEVM { let exe = &self.exe.as_ref().unwrap(); macro_rules! hex2str { ($val:expr) => { - serde_json::to_string(&$val).unwrap() + format!("{:#066x}", $val) }; } @@ -892,18 +953,13 @@ impl TinyEVM { /// - `config`: A json string serialized for [`InstrumentConfig`](https://github.com/sbip-sg/revm/blob/6f7ac687a22f67462999ca132ede8d116bd7feb9/crates/revm/src/bug.rs#L153) pub fn configure(&mut self, config: &REVMConfig) -> Result<()> { let config = config.to_iconfig()?; - let db = &mut self.exe.as_mut().unwrap().context.evm.db; - db.instrument_config = Some(config); + self.bug_inspector_mut().instrument_config = config; Ok(()) } /// Get current runtime instrumentation configuration pub fn get_instrument_config(&self) -> Result { - let db = &self.exe.as_ref().unwrap().context.evm.db; - let r = &db - .instrument_config - .as_ref() - .ok_or_else(|| eyre!("Instrumentation config not set"))?; + let r = &self.bug_inspector().instrument_config; Ok(REVMConfig::from(r)) } @@ -1031,7 +1087,7 @@ impl TinyEVM { /// Take a snapshot of an account, raise error if account does not exist in db pub fn take_snapshot(&mut self, address: String) -> Result<()> { let addr = Address::from_str(&address)?; - let db = &self.exe.as_ref().unwrap().context.evm.db; + let db = self.db(); if let Some(account) = db.accounts.get(&addr) { self.snapshots.insert(addr, account.clone()); Ok(()) @@ -1059,19 +1115,55 @@ impl TinyEVM { } pub fn clear_instrumentation(&mut self) { - let db = &mut self.exe.as_mut().unwrap().context.evm.db; - db.instrument_data.bug_data.clear(); - db.instrument_data.created_addresses.clear(); - db.instrument_data.heuristics = Default::default(); + let bug_inspector = self.bug_inspector_mut(); + bug_inspector.bug_data.clear(); + bug_inspector.created_addresses.clear(); + bug_inspector.heuristics = Default::default(); + self.log_inspector_mut().traces.clear(); + self.log_inspector_mut().logs.clear(); } /// Restore a snapshot for an account, raise error if there is no snapshot for the account pub fn restore_snapshot(&mut self, address: String) -> Result<()> { let addr = Address::from_str(&address)?; - let db = &mut self.exe.as_mut().unwrap().context.evm.db; - let account = self.snapshots.get(&addr).context("No snapshot found")?; - db.accounts.insert(addr, account.clone()); + let account = { + self.snapshots + .get(&addr) + .context("No snapshot found")? + .clone() + }; + self.db_mut().accounts.insert(addr, account); + Ok(()) + } + + /// Take global snapshot of all accounts + pub fn take_global_snapshot(&mut self) -> Result { + let db = self.db(); + let snapshot = db.clone(); + let id = Uuid::new_v4(); + self.global_snapshot.insert(id, snapshot); + Ok(id.to_string()) + } + + pub fn restore_global_snapshot( + &mut self, + snapshot_id: String, + keep_snapshot: bool, + ) -> Result<()> { + let id = Uuid::parse_str(&snapshot_id)?; + + if keep_snapshot { + let snapshot = self.global_snapshot.get(&id).context("No snapshot found")?; + *self.db_mut() = snapshot.clone(); + } else { + let snapshot = self + .global_snapshot + .remove(&id) + .context("No snapshot found")?; + let _ = replace(self.db_mut(), snapshot); + } + Ok(()) } } @@ -1080,7 +1172,7 @@ impl TinyEVM { /// REVM::InstrumentConfig #[pyclass(set_all, get_all)] pub struct REVMConfig { - /// Master switch to toggle instrumentation + /// Enable the bug detector instrumentation pub enabled: bool, /// Enable recording seen PCs by current contract address pub pcs_by_address: bool, @@ -1125,8 +1217,8 @@ impl REVMConfig { }; Ok(InstrumentConfig { - target_address, enabled: self.enabled, + target_address, pcs_by_address: self.pcs_by_address, heuristics: self.heuristics, record_branch_for_target_only: self.record_branch_for_target_only, diff --git a/src/response.rs b/src/response.rs index 932b4c1..2bd662d 100644 --- a/src/response.rs +++ b/src/response.rs @@ -14,8 +14,10 @@ use std::collections::HashMap as StdHashMap; use std::collections::HashSet as StdHashSet; use crate::{ - instrument::bug::*, - logs::{CallTrace, Log}, + instrument::{ + bug::*, + log_inspector::{CallTrace, Log}, + }, ruint_u256_to_bigint, trim_prefix, }; use primitive_types::H160; @@ -57,12 +59,13 @@ pub struct WrappedBug { #[derive(Clone, Debug)] pub struct WrappedMissedBranch { /// Previous program counter - // pub prev_pc: usize, pub prev_pc: usize, - /// Missed program counter - pub pc: usize, + /// Destination pc if condition is true + pub dest_pc: usize, + pub cond: bool, /// Distiance required to reach the missed branch pub distance: BigInt, + pub address_index: isize, } /// Wrapper around Heuristics @@ -103,8 +106,10 @@ impl From for WrappedHeuristics { .iter() .map(|x| WrappedMissedBranch { prev_pc: x.prev_pc, - pc: x.pc, + dest_pc: x.dest_pc, + cond: x.cond, distance: ruint_u256_to_bigint(&x.distance), + address_index: x.address_index, }) .collect(); let mut sha3_mapping = StdHashMap::new(); diff --git a/tests/contracts/snapshots.compiled.json b/tests/contracts/snapshots.compiled.json new file mode 100644 index 0000000..fed1013 --- /dev/null +++ b/tests/contracts/snapshots.compiled.json @@ -0,0 +1 @@ +{"contracts":{"tests/contracts/snapshots.sol:Test":{"abi":[{"inputs":[],"name":"counter","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256","name":"","type":"int256"}],"name":"counter1","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256","name":"","type":"int256"}],"name":"counter2","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"f_fast","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"f_slow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fast_seq","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"slow_seq","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"nonpayable","type":"function"}],"bin":"6080604052600160025534801561001557600080fd5b50610423806100256000396000f3fe608060405234801561001057600080fd5b506004361061007d5760003560e01c80635a6ce6441161005b5780635a6ce644146101005780635bd11ee01461010a57806361bc221a1461012857806391575026146101465761007d565b80631ac7c6c5146100825780632b208f68146100a05780632d7ee760146100d0575b600080fd5b61008a610150565b60405161009791906102b9565b60405180910390f35b6100ba60048036038101906100b59190610305565b610185565b6040516100c791906102b9565b60405180910390f35b6100ea60048036038101906100e59190610305565b61019d565b6040516100f791906102b9565b60405180910390f35b6101086101b5565b005b61011261021d565b60405161011f91906102b9565b60405180910390f35b610130610252565b60405161013d91906102b9565b60405180910390f35b61014e610258565b005b600080600090505b600581121561017c576101696101b5565b808061017490610361565b915050610158565b50600254905090565b60016020528060005260406000206000915090505481565b60006020528060005260406000206000915090505481565b60005b603281121561021a576001600260008282546101d491906103a9565b9250508190555060016000806002548152602001908152602001600020600082825461020091906103a9565b92505081905550808061021290610361565b9150506101b8565b50565b600080600090505b600581121561024957610236610258565b808061024190610361565b915050610225565b50600254905090565b60025481565b60016002600082825461026b91906103a9565b9250508190555060016000806002548152602001908152602001600020600082825461029791906103a9565b92505081905550565b6000819050919050565b6102b3816102a0565b82525050565b60006020820190506102ce60008301846102aa565b92915050565b600080fd5b6102e2816102a0565b81146102ed57600080fd5b50565b6000813590506102ff816102d9565b92915050565b60006020828403121561031b5761031a6102d4565b5b6000610329848285016102f0565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061036c826102a0565b91507f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361039e5761039d610332565b5b600182019050919050565b60006103b4826102a0565b91506103bf836102a0565b9250828201905082811215600083121683821260008412151617156103e7576103e6610332565b5b9291505056fea26469706673582212206e191165fdf82df5652c2babfba503ef181f18d0c5d6245eec3918dc3c73997164736f6c63430008130033","bin-runtime":"608060405234801561001057600080fd5b506004361061007d5760003560e01c80635a6ce6441161005b5780635a6ce644146101005780635bd11ee01461010a57806361bc221a1461012857806391575026146101465761007d565b80631ac7c6c5146100825780632b208f68146100a05780632d7ee760146100d0575b600080fd5b61008a610150565b60405161009791906102b9565b60405180910390f35b6100ba60048036038101906100b59190610305565b610185565b6040516100c791906102b9565b60405180910390f35b6100ea60048036038101906100e59190610305565b61019d565b6040516100f791906102b9565b60405180910390f35b6101086101b5565b005b61011261021d565b60405161011f91906102b9565b60405180910390f35b610130610252565b60405161013d91906102b9565b60405180910390f35b61014e610258565b005b600080600090505b600581121561017c576101696101b5565b808061017490610361565b915050610158565b50600254905090565b60016020528060005260406000206000915090505481565b60006020528060005260406000206000915090505481565b60005b603281121561021a576001600260008282546101d491906103a9565b9250508190555060016000806002548152602001908152602001600020600082825461020091906103a9565b92505081905550808061021290610361565b9150506101b8565b50565b600080600090505b600581121561024957610236610258565b808061024190610361565b915050610225565b50600254905090565b60025481565b60016002600082825461026b91906103a9565b9250508190555060016000806002548152602001908152602001600020600082825461029791906103a9565b92505081905550565b6000819050919050565b6102b3816102a0565b82525050565b60006020820190506102ce60008301846102aa565b92915050565b600080fd5b6102e2816102a0565b81146102ed57600080fd5b50565b6000813590506102ff816102d9565b92915050565b60006020828403121561031b5761031a6102d4565b5b6000610329848285016102f0565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061036c826102a0565b91507f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361039e5761039d610332565b5b600182019050919050565b60006103b4826102a0565b91506103bf836102a0565b9250828201905082811215600083121683821260008412151617156103e7576103e6610332565b5b9291505056fea26469706673582212206e191165fdf82df5652c2babfba503ef181f18d0c5d6245eec3918dc3c73997164736f6c63430008130033","srcmap":"56:682:0:-:0;;;191:1;167:25;;56:682;;;;;;;;;;;;;;;;","srcmap-runtime":"56:682:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;590:146;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;121:40;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;75;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;289:144;;;:::i;:::-;;439:146;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;167:25;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;198:86;;;:::i;:::-;;590:146;626:6;648:8;659:1;648:12;;643:63;666:1;662;:5;643:63;;;687:8;:6;:8::i;:::-;669:3;;;;;:::i;:::-;;;;643:63;;;;722:7;;715:14;;590:146;:::o;121:40::-;;;;;;;;;;;;;;;;;:::o;75:::-;;;;;;;;;;;;;;;;;:::o;289:144::-;328:8;323:104;346:2;342:1;:6;323:104;;;379:1;368:7;;:12;;;;;;;:::i;:::-;;;;;;;;415:1;394:8;:17;403:7;;394:17;;;;;;;;;;;;:22;;;;;;;:::i;:::-;;;;;;;;350:3;;;;;:::i;:::-;;;;323:104;;;;289:144::o;439:146::-;475:6;497:8;508:1;497:12;;492:63;515:1;511;:5;492:63;;;536:8;:6;:8::i;:::-;518:3;;;;;:::i;:::-;;;;492:63;;;;571:7;;564:14;;439:146;:::o;167:25::-;;;;:::o;198:86::-;244:1;233:7;;:12;;;;;;;:::i;:::-;;;;;;;;276:1;255:8;:17;264:7;;255:17;;;;;;;;;;;;:22;;;;;;;:::i;:::-;;;;;;;;198:86::o;7:76:1:-;43:7;72:5;61:16;;7:76;;;:::o;89:115::-;174:23;191:5;174:23;:::i;:::-;169:3;162:36;89:115;;:::o;210:218::-;301:4;339:2;328:9;324:18;316:26;;352:69;418:1;407:9;403:17;394:6;352:69;:::i;:::-;210:218;;;;:::o;515:117::-;624:1;621;614:12;761:120;833:23;850:5;833:23;:::i;:::-;826:5;823:34;813:62;;871:1;868;861:12;813:62;761:120;:::o;887:137::-;932:5;970:6;957:20;948:29;;986:32;1012:5;986:32;:::i;:::-;887:137;;;;:::o;1030:327::-;1088:6;1137:2;1125:9;1116:7;1112:23;1108:32;1105:119;;;1143:79;;:::i;:::-;1105:119;1263:1;1288:52;1332:7;1323:6;1312:9;1308:22;1288:52;:::i;:::-;1278:62;;1234:116;1030:327;;;;:::o;1363:180::-;1411:77;1408:1;1401:88;1508:4;1505:1;1498:15;1532:4;1529:1;1522:15;1549:231;1587:3;1610:23;1627:5;1610:23;:::i;:::-;1601:32;;1655:66;1648:5;1645:77;1642:103;;1725:18;;:::i;:::-;1642:103;1772:1;1765:5;1761:13;1754:20;;1549:231;;;:::o;1786:375::-;1825:3;1844:19;1861:1;1844:19;:::i;:::-;1839:24;;1877:19;1894:1;1877:19;:::i;:::-;1872:24;;1919:1;1916;1912:9;1905:16;;2117:1;2112:3;2108:11;2101:19;2097:1;2094;2090:9;2086:35;2069:1;2064:3;2060:11;2055:1;2052;2048:9;2041:17;2037:35;2021:110;2018:136;;;2134:18;;:::i;:::-;2018:136;1786:375;;;;:::o"}},"sourceList":["tests/contracts/snapshots.sol"],"version":"0.8.19+commit.7dd6d404.Linux.g++"} diff --git a/tests/contracts/snapshots.sol b/tests/contracts/snapshots.sol new file mode 100644 index 0000000..17e983c --- /dev/null +++ b/tests/contracts/snapshots.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; +contract Test{ + mapping (int256=>int256) public counter1; + mapping (int256=>int256) public counter2; + int256 public counter = 1; + function f_fast() public { + counter += 1; + counter1[counter] += 1; + } + function f_slow() public{ + for (int256 i = 0; i < 50; i++){ + counter += 1; + counter1[counter] += 1; + } + } + + function fast_seq() public returns (int256){ + for (int256 i = 0; i < 5; i++){ + f_fast(); + } + return counter; + } + function slow_seq() public returns (int256){ + for (int256 i = 0; i < 5; i++){ + f_slow(); + } + return counter; + } +} diff --git a/tests/revm_test.rs b/tests/revm_test.rs index 20ab96d..7e6579a 100644 --- a/tests/revm_test.rs +++ b/tests/revm_test.rs @@ -8,12 +8,14 @@ use primitive_types::{H160, H256}; use revm::interpreter::opcode::{self, CREATE, CREATE2, SELFDESTRUCT}; use revm::primitives::Address; use ruint::aliases::U256; +use std::collections::HashSet; use std::convert::TryInto; +use std::env; use std::iter::repeat_with; use std::ops::Add; use std::str::FromStr; -use std::{collections::HashSet, env}; -use tinyevm::instrument::bug::{Bug, BugType, InstrumentConfig, MissedBranch}; +use tinyevm::instrument::bug::{Bug, BugType, MissedBranch}; +use tracing::warn; use tinyevm::{ enable_tracing, fn_sig_to_prefix, ruint_u256_to_bigint, trim_prefix, TinyEVM, TX_GAS_LIMIT, @@ -48,14 +50,7 @@ macro_rules! deploy_hex { let bytecode_hex = include_str!($hex_path); let bytecode = hex::decode(bytecode_hex).unwrap(); - let resp = $vm.deploy_helper( - *OWNER, - bytecode, - UZERO, - false, - None, - Some(*CONTRACT_ADDRESS), - ); + let resp = $vm.deploy_helper(*OWNER, bytecode, UZERO, None, Some(*CONTRACT_ADDRESS)); assert!( resp.is_ok(), @@ -85,6 +80,8 @@ fn check_expected_bugs_are_found(expected: Vec<(BugType, usize)>, found: Vec = expected_bugs.difference(&found_bugs).collect(); assert_eq!(0, diff.len(), "Expected bugs {diff:#?} should be found"); } @@ -107,6 +104,10 @@ fn t_erc20_balance_query(vm: &mut TinyEVM, address: Address, expected_balance: U assert_eq!(expected_balance, balance); } +fn setup() { + let _ = enable_tracing(); +} + /// Convenient function create binary for the solidty function: transfer(address,uint256) fn make_transfer_bin(to: Address, amount: U256) -> Vec { let prefix = fn_sig_to_prefix("transfer(address,uint256)"); @@ -183,7 +184,7 @@ fn single_bugtype_test_helper( let bytecode = hex::decode(contract_deploy_hex).unwrap(); let resp = vm - .deploy_helper(owner, bytecode, UZERO, false, None, None) + .deploy_helper(owner, bytecode, UZERO, None, None) .unwrap(); let address = Address::from_slice(&resp.data); @@ -219,6 +220,7 @@ fn single_bugtype_test_helper( #[test] fn test_overflow() { + setup(); let u256_max_as_hex = format!("{:#x}", U256::MAX); let contract_hex = include_str!("../tests/contracts/IntegerOverflowAdd_deploy.hex"); let num_runs = 1; @@ -297,6 +299,7 @@ fn test_timestamp_and_block_number() { #[test] fn test_tx_origin() { + setup(); let contract_hex = include_str!("../tests/contracts/tx_origin.hex"); let fn_args = ""; let fn_sig = "run()"; @@ -308,6 +311,7 @@ fn test_tx_origin() { #[test] fn test_tx_origin_v2() { + setup(); let contract_hex = include_str!("../tests/contracts/test_txorigin.hex"); let fn_args = ""; let fn_sig = "txorigin()"; @@ -326,7 +330,8 @@ fn test_tx_origin_v2() { #[test] fn test_call_trace() { - deploy_hex!("../tests/contracts/calls_trace.hex", exe, address); + setup(); + deploy_hex!("../tests/contracts/calls_trace.hex", vm, address); let tests = vec![ ("always_fail()", vec![(BugType::RevertOrInvalid, 167)], true), @@ -359,9 +364,9 @@ fn test_call_trace() { let fn_hex = fn_sig_to_prefix(fn_sig); let data = hex::decode(fn_hex).unwrap(); let resp = - exe.contract_call_helper(Address::new(address.0), *OWNER, data.clone(), UZERO, None); + vm.contract_call_helper(Address::new(address.0), *OWNER, data.clone(), UZERO, None); assert_eq!(expect_revert, !resp.success); - let bugs = &exe.bug_data(); + let bugs = &vm.bug_data(); let bugs: Vec<_> = bugs.iter().cloned().collect(); check_expected_bugs_are_found(expected_bugs, bugs.to_vec()); } @@ -373,29 +378,31 @@ fn test_deterministic_deploy() { let contract_deploy_bin = hex::decode(contract_deploy_hex).unwrap(); let mut vm = TinyEVM::default(); let c1 = vm - .deploy_helper( - *OWNER, - contract_deploy_bin.clone(), - UZERO, - false, - None, - None, - ) + .deploy_helper(*OWNER, contract_deploy_bin.clone(), UZERO, None, None) .unwrap(); - assert!(c1.success, "Deploy use salt 1 should succeed: {:?}", &c1); + assert!( + c1.success, + "Deploy by initial nonce should succeed: {:?}", + &c1 + ); let c2 = vm - .deploy_helper(*OWNER, contract_deploy_bin, UZERO, false, None, None) + .deploy_helper(*OWNER, contract_deploy_bin, UZERO, None, None) .unwrap(); - assert!(c2.success, "Deploy use salt 2 should succeed: {:?}", &c2); + assert!( + c2.success, + "Deploy by auto updated nonce should succeed: {:?}", + &c2 + ); assert_ne!(c1.data, c2.data, "Address of c1 and c2 should not equal"); } #[test] fn test_deterministic_deploy_overwrite() -> Result<()> { + setup(); let contract_deploy_hex = include_str!("../tests/contracts/coverage.hex"); let contract_deploy_bin = hex::decode(contract_deploy_hex).unwrap(); let target_address = Address::from_slice(H160::random().as_bytes()); @@ -406,7 +413,6 @@ fn test_deterministic_deploy_overwrite() -> Result<()> { *OWNER, contract_deploy_bin.clone(), UZERO, - false, None, force_address, ) @@ -426,6 +432,7 @@ fn test_deterministic_deploy_overwrite() -> Result<()> { let c1_code = { let accounts = &vm.exe.as_ref().unwrap().db().accounts; + println!("accounts: {:?}", accounts); let account = accounts .get(&target_address) .context("Expecting first account has non nil value")?; @@ -433,14 +440,7 @@ fn test_deterministic_deploy_overwrite() -> Result<()> { }; let c2 = vm - .deploy_helper( - *OWNER, - contract_deploy_bin, - UZERO, - true, - None, - force_address, - ) + .deploy_helper(*OWNER, contract_deploy_bin, UZERO, None, force_address) .unwrap(); assert!( @@ -449,9 +449,11 @@ fn test_deterministic_deploy_overwrite() -> Result<()> { c2 ); + let c2_address = Address::from_slice(&c2.data); + assert_eq!( - c1.data, c2.data, - "Deploy same contract with same salt should have same address" + c1_address, c2_address, + "Deploy same contract to the same forced addess should result in the same address" ); let c2_code = { @@ -466,57 +468,85 @@ fn test_deterministic_deploy_overwrite() -> Result<()> { Ok(()) } -#[test] -fn test_heuristics() { - deploy_hex!("../tests/contracts/heuristics.hex", vm, address); +fn test_heuristics_inner( + input: u64, // `i` in the function `coverage(uint256 i)` + expected_missed_branches: Vec, // expected list of jumpi + expected_coverages: Vec, // expected list of coverage PCs +) { + deploy_hex!("../tests/contracts/heuristics.hex", exe, address); let fn_sig = "coverage(uint256)"; let fn_sig_hex = fn_sig_to_prefix(fn_sig); - let fn_args_hex = format!("{:0>64x}", U256::from(50)); - - let expected_missed_branches: Vec = vec![ - // (prev_pc, pc, distance) - (24, 40, 0), // Function signature selection input size check (4 bytes) - (45, 61, 1), // Function signature selection - (65, 120, 0x26df), - (127, 136, 0x33), - (143, 152, 0x30), - ] - .into_iter() - .map(|(prev_pc, pc, distance)| (prev_pc, pc, U256::from(distance as u64)).into()) - .collect(); + let fn_args_hex = format!("{:0>64x}", U256::from(input)); let fn_hex = format!("{}{}", fn_sig_hex, fn_args_hex); let tx_data = hex::decode(fn_hex).unwrap(); - let resp = vm.contract_call_helper(Address::new(address.0), *OWNER, tx_data, UZERO, None); + let resp = exe.contract_call_helper(Address::new(address.0), *OWNER, tx_data, UZERO, None); - assert!(resp.success, "Transaction should succeed."); + assert!( + resp.success, + "Transaction should succeed with input {}", + input + ); let heuristics = resp.heuristics; - let missed_branches: Vec<_> = heuristics.missed_branches.into_iter().collect(); - let coverage: Vec = heuristics.coverage.into_iter().collect(); - - assert_eq!( - expected_missed_branches.len(), - missed_branches.len(), - "All missed branches should be found" - ); + let missed_branches: Vec<_> = heuristics.missed_branches.into_iter().skip(4).collect(); + let coverage: Vec = heuristics + .coverage + .into_iter() + .skip(4) // skip 4 from function selector operations + .collect(); assert_eq!( expected_missed_branches, missed_branches, - "All missed branches should be found with expected distances" + "All missed branches should be found with expected distances with input {}", + input ); assert_eq!( - coverage, - vec![15, 24, 45, 65, 127, 143, 159], - "List of coverage PCs should match " + expected_coverages, coverage, + "List of coverage PCs should match with input {}", + input ); } +#[test] +fn test_heuristics() { + setup(); + // Test coverage(200) + let input = 200; + let expected_missed_branches: Vec = vec![ + // (prev_pc, pc, is_jump_to_target, distance) + // skips 4 from function selector operations + (119, 127, true, 0x2649), + (135, 143, false, 0x64), + ] + .into_iter() + .map(|(prev_pc, pc, cond, distance)| (prev_pc, pc, cond, U256::from(distance as u64), 0).into()) + .collect(); + + let expected_coverages = vec![127, 136]; + test_heuristics_inner(input, expected_missed_branches, expected_coverages); + + // Test coverage(50) + let input = 50; + let expected_missed_branches: Vec = vec![ + // (prev_pc, pc, is_jump_to_target, distance) + (119, 127, true, 0x26df), + (135, 143, true, 0x33), + (151, 159, true, 0x30), + ] + .into_iter() + .map(|(prev_pc, pc, cond, distance)| (prev_pc, pc, cond, U256::from(distance as u64), 0).into()) + .collect(); + + let expected_coverages = vec![127, 143, 159]; + test_heuristics_inner(input, expected_missed_branches, expected_coverages); +} + #[test] fn test_heuristics_signed_int() { deploy_hex!("../tests/contracts/heuristics-signed-int.hex", exe, address); @@ -533,14 +563,13 @@ fn test_heuristics_signed_int() { let expected_missed_branches: Vec = vec![ // (prev_pc, pc, distance) - // (26, 43, 0), // skip two from function selector operations - // (48, 66, 1), - (70, 156, 9950), - (195, 236, 51), - (275, 316, 48), + // skips 4 jumpis: callvalue, calldatasize, selector, calldata argument size check + (155, 195, 9950), + (235, 275, 51), + (315, 355, 48), ] .into_iter() - .map(|(prev_pc, pc, distance)| (prev_pc, pc, U256::from(distance as u64)).into()) + .map(|(prev_pc, pc, distance)| (prev_pc, pc, true, U256::from(distance as u64), 0).into()) .collect(); let fn_hex = format!("{}{}", fn_sig_hex, fn_args_hex); @@ -558,7 +587,7 @@ fn test_heuristics_signed_int() { .heuristics .missed_branches .into_iter() - .skip(2) + .skip(4) .collect(); assert_eq!( @@ -586,7 +615,7 @@ fn test_bug_data_in_deploy() { let bytecode = hex::decode(format!("{}{}", contract_hex, constructor_args_hex)).unwrap(); let resp = vm - .deploy_helper(owner, bytecode, UZERO, false, None, None) + .deploy_helper(owner, bytecode, UZERO, None, None) .unwrap(); assert!(resp.success, "Contract deploy should succeed."); @@ -625,7 +654,7 @@ fn test_deploy_with_args_and_value() { let bytecode = hex::decode(format!("{}{}", contract_hex, constructor_args_hex)).unwrap(); let resp = vm - .deploy_helper(owner, bytecode, value, false, None, None) + .deploy_helper(owner, bytecode, value, None, None) .unwrap(); println!("resp: {resp:?}"); @@ -672,6 +701,7 @@ fn test_div_zero() { #[test] fn test_mod_zero() { + setup(); let tests = vec![ (U256::from(1), Some((BugType::IntegerModByZero, 149)), false), (U256::from(2), Some((BugType::IntegerModByZero, 178)), false), @@ -686,7 +716,7 @@ fn test_mod_zero() { #[test] fn test_gas_usage() { - // let _ = enable_tracing(); + setup(); let owner = *OWNER; // deploy_hex!("../tests/contracts/gasusage.hex", exe, address); @@ -694,7 +724,7 @@ fn test_gas_usage() { let bytecode_hex = include_str!("../tests/contracts/gasusage.hex"); let bytecode = hex::decode(bytecode_hex).unwrap(); - let resp = vm.deploy_helper(*OWNER, bytecode, UZERO, false, None, None); + let resp = vm.deploy_helper(*OWNER, bytecode, UZERO, None, None); assert!(resp.is_ok(), "Contract deploy should succeed."); @@ -764,7 +794,7 @@ fn test_set_get_storage() { #[test] fn test_set_get_code() { - // let _ = enable_tracing(); + setup(); let owner = Address::new(H160::random().0); let mut vm = TinyEVM::default(); @@ -789,14 +819,21 @@ fn test_set_get_code() { #[test] fn test_exp_overflow() { + setup(); let owner = *OWNER; - let mut vm = TinyEVM::default(); - deploy_hex!("../tests/contracts/exp_overflow.hex", exe, address); + deploy_hex!("../tests/contracts/exp_overflow.hex", vm, address); let fn_sig = "exp(uint256)"; let fn_sig_hex = fn_sig_to_prefix(fn_sig); let bin = format!("{}{:0>64x}", fn_sig_hex, 200); let bin = hex::decode(bin).unwrap(); + + println!("Calling deployed contract: {:?}", address); + println!( + "Current accounts: {:?}", + vm.exe.as_ref().unwrap().db().accounts + ); + let resp = vm.contract_call_helper(Address::new(address.0), owner, bin, UZERO, None); assert!( @@ -809,14 +846,12 @@ fn test_exp_overflow() { let resp = vm.contract_call_helper(Address::new(address.0), owner, bin, UZERO, None); + let bugs = &resp.bug_data; + assert!( - &resp - .bug_data - .clone() - .into_iter() - .any(|b| b.opcode == opcode::EXP), + &bugs.iter().any(|b| b.opcode == opcode::EXP), "Expecting exp overflow in {:?}", - &resp.bug_data + bugs ); } @@ -842,13 +877,13 @@ fn single_run_test_helper(contract_bin_hex: &str, fn_sig: &str, tests: Vec = vec![501, 554, 561].into_iter().collect(); @@ -944,9 +980,9 @@ fn test_tod() { let bin = format!("{}{}", fn_sig_to_prefix("write_a(uint256)"), arg_hex); let bin = hex::decode(bin).unwrap(); - let resp = exe.contract_call_helper(Address::new(addr.0), owner, bin, UZERO, None); + let resp = vm.contract_call_helper(Address::new(addr.0), owner, bin, UZERO, None); assert!(resp.success, "Call should succeed"); - let bugs = exe.bug_data().clone(); + let bugs = vm.bug_data().clone(); println!("{:?}", bugs); @@ -980,10 +1016,9 @@ fn test_get_set_balance() { let balance = vm.get_eth_balance(owner).unwrap(); assert_eq!(UZERO, balance, "Expect empty account has zero balance"); - vm.set_account_balance(owner.into(), target_balance) - .unwrap(); + vm.set_account_balance(owner, target_balance).unwrap(); - let balance = vm.get_eth_balance(owner.into()).unwrap(); + let balance = vm.get_eth_balance(owner).unwrap(); assert_eq!( balance, target_balance, "Expect changed to the target balance" @@ -993,12 +1028,12 @@ fn test_get_set_balance() { let bytecode = include_str!("../tests/contracts/balance.hex"); let bytecode = hex::decode(bytecode).unwrap(); let resp = vm - .deploy_helper(owner, bytecode, UZERO, false, None, None) + .deploy_helper(owner, bytecode, UZERO, None, None) .unwrap(); assert!(resp.success, "Deployment should succeed"); let addr = Address::from_slice(&resp.data); - vm.set_account_balance(addr.into(), target_balance).unwrap(); + vm.set_account_balance(addr, target_balance).unwrap(); let bin = hex::decode(fn_sig_to_prefix("selfbalance()")).unwrap(); let resp = vm.contract_call_helper(addr, owner, bin, UZERO, None); @@ -1027,6 +1062,7 @@ fn test_get_set_balance() { #[test] fn test_selfdestruct_and_create() { + setup(); deploy_hex!("../tests/contracts/self_destruct.hex", vm, addr); let bin = hex::decode(fn_sig_to_prefix("kill()")).unwrap(); @@ -1076,10 +1112,7 @@ fn test_seen_pcs() { ); assert!(resp.success, "Call error {:?}", resp); - let seen_pcs = &vm - .instrument_data() - .pcs_by_address - .get(&Address::new(address.0)); + let seen_pcs = &vm.pcs_by_address().get(&Address::new(address.0)); assert!( seen_pcs.is_some(), "Seen PCs should be found for the target contract " @@ -1092,15 +1125,11 @@ fn test_seen_pcs() { #[test] fn test_runtime_configuration() { + setup(); deploy_hex!("../tests/contracts/contract_creation_B.hex", vm, address); let address = Address::new(address.0); - let config = InstrumentConfig { - pcs_by_address: false, - ..Default::default() - }; - - // vm.db.instrument_config = Some(config); + vm.instrument_config_mut().pcs_by_address = false; vm.set_account_balance( *OWNER, @@ -1119,7 +1148,7 @@ fn test_runtime_configuration() { ); assert!(resp.success, "Call error {:?}", resp); - let seen_pcs = &vm.instrument_data().pcs_by_address.get(&address); + let seen_pcs = &vm.pcs_by_address().get(&address); assert!( seen_pcs.is_none() || seen_pcs.unwrap().is_empty(), "No PCs by address should be recorded" @@ -1177,6 +1206,7 @@ fn test_reset_storage() { #[test] fn test_sha3_mapping() { + setup(); deploy_hex!("../tests/contracts/sha3_mapping.hex", vm, addr); let addr = Address::new(addr.0); @@ -1213,31 +1243,33 @@ fn test_sha3_mapping() { #[test] fn test_seen_addresses() { + setup(); let mut vm = TinyEVM::default(); - - { - // vm.env.block.number = U256::from(100u64); - } + let r = vm.set_env_field_value("block_number".into(), format!("{:0>64x}", U256::from(1u64))); + assert!(r.is_ok(), "Set block number should succeed"); let bytecode = include_str!("./contracts/contract_addresses_A.hex"); let bytecode = hex::decode(bytecode).unwrap(); let resp = vm - .deploy_helper(*OWNER, bytecode, UZERO, false, None, None) + .deploy_helper(*OWNER, bytecode, UZERO, None, None) .unwrap(); assert!(resp.success, "Deploy contract A should succeed"); let addr_a = Address::from_slice(&resp.data); - // if let Some(ref mut config) = vm.db.instrument_config { - // config.record_branch_for_target_only = true; - // config.target_address = addr_a; - // } + println!("address A: {:?}", addr_a); + + { + let config = vm.instrument_config_mut(); + config.record_branch_for_target_only = true; + config.target_address = addr_a; + } let bytecode = include_str!("./contracts/contract_addresses_B.hex"); let bytecode = format!("{}{:0>64}", bytecode, addr_a.encode_hex::()); println!("Deploying contract B with bytecode: {}", bytecode); let bytecode = hex::decode(bytecode).unwrap(); let resp = vm - .deploy_helper(*OWNER, bytecode, UZERO, false, None, None) + .deploy_helper(*OWNER, bytecode, UZERO, None, None) .unwrap(); assert!( resp.success, @@ -1247,6 +1279,8 @@ fn test_seen_addresses() { let addr = Address::from_slice(&resp.data); + println!("address B: {:?}", addr); + let prefix = fn_sig_to_prefix("getBlockNumber()"); let args = ""; @@ -1272,6 +1306,7 @@ fn test_seen_addresses() { #[test] fn test_distance_signed() { + setup(); deploy_hex!("../tests/contracts/test_distance_signed.hex", vm, address); let address = Address::new(address.0); let fn_sig = "sign_distance(int256)"; @@ -1306,15 +1341,26 @@ fn test_distance_signed() { .map(|b| b.distance) .collect::>(); - println!("Missed branches: {:?}", missed_branches_distance); + println!("Missed branches distances: {:?}", missed_branches_distance); - assert!(expected_distances + let failed_to_find = expected_distances .iter() - .all(|d| { missed_branches_distance.contains(&U256::from(*d)) })); + .filter(|d| !(missed_branches_distance.contains(&U256::from(**d)))) + .collect::>(); + + println!( + "Failed to find missed branches distances: {:?}", + failed_to_find + ); + assert!( + failed_to_find.is_empty(), + "All expected distances should be found" + ); } #[test] fn test_peephole_optimized_if_equal() { + setup(); deploy_hex!( "../tests/contracts/test_peephole_optimized.hex", vm, @@ -1327,7 +1373,8 @@ fn test_peephole_optimized_if_equal() { let input = U256::from(1); let fn_args_hex = format!("{:0>64x}", input); - let expected_missed_branches: (usize, usize, U256) = (317, 167, U256::from(0x2007)); + let expected_missed_branches: (usize, usize, U256) = (166, 181, U256::from(0x2007)); + // [MissedBranch { prev_pc: 11, cond: true, dest_pc: 16, distance: 115792089237316195423570985008687907853269984665640564039457584007913129639935, address_index: 0 }, MissedBranch { prev_pc: 25, cond: false, dest_pc: 65, distance: 33, address_index: 0 }, MissedBranch { prev_pc: 42, cond: true, dest_pc: 70, distance: 1, address_index: 0 }, MissedBranch { prev_pc: 354, cond: true, dest_pc: 363, distance: 1, address_index: 0 }, MissedBranch { prev_pc: 312, cond: true, dest_pc: 317, distance: 1, address_index: 0 }, MissedBranch { prev_pc: 166, cond: true, dest_pc: 181, distance: 8199, address_index: 0 }] let fn_hex = format!("{}{}", fn_sig_hex, fn_args_hex); @@ -1342,9 +1389,10 @@ fn test_peephole_optimized_if_equal() { let found_missed_branch_at_func1 = resp.heuristics.missed_branches.iter().any( |MissedBranch { prev_pc, - pc, + dest_pc, distance, - }| { (*prev_pc, *pc, *distance) == expected_missed_branches }, + .. + }| { (*prev_pc, *dest_pc, *distance) == expected_missed_branches }, ); assert!( found_missed_branch_at_func1, @@ -1354,6 +1402,12 @@ fn test_peephole_optimized_if_equal() { #[test] fn test_fork() -> Result<()> { + setup(); + if env::var("TINYEVM_CI_TESTS").is_ok() { + warn!("Skipping tests on CI"); + return Ok(()); + } + let fork_url = Some("https://eth.llamarpc.com".into()); let block_id = Some(17869485); @@ -1381,13 +1435,17 @@ fn test_fork() -> Result<()> { #[test] fn test_call_forked_contract_from_local_contract() -> Result<()> { - let _ = enable_tracing(); + setup(); + if env::var("TINYEVM_CI_TESTS").is_ok() { + warn!("Skipping tests on CI"); + return Ok(()); + } + let bin = include_str!("../tests/contracts/test_fork.hex"); let fork_url = Some("https://bscrpc.com".into()); let block_id = Some(0x1e08bd6); let mut evm = TinyEVM::new(fork_url, block_id)?; - enable_tracing()?; let resp = evm.deploy(bin.into(), None)?; @@ -1454,12 +1512,17 @@ fn test_call_forked_contract_from_local_contract() -> Result<()> { #[test] fn test_sturdy_hack() -> Result<()> { + setup(); + if env::var("TINYEVM_CI_TESTS").is_ok() { + warn!("Skipping tests on CI"); + return Ok(()); + } + let bin = include_str!("../tests/contracts/SturdyFinance_ReadonlyRE.hex"); let fork_url = Some("https://eth.llamarpc.com".into()); let block_id = Some(17_460_609); let mut evm = TinyEVM::new(fork_url, block_id)?; - enable_tracing()?; let resp = evm.deploy(bin.into(), None)?; @@ -1507,25 +1570,24 @@ fn test_sturdy_hack() -> Result<()> { #[test] fn test_events() -> Result<()> { - env::remove_var("TINYEVM_CALL_TRACE_ENABLED"); let bin = include_str!("../tests/contracts/TestEvents.hex"); - let mut evm = TinyEVM::new_offline()?; - let resp = evm.deploy(bin.into(), None)?; + let mut vm = TinyEVM::default(); + let resp = vm.deploy(bin.into(), None)?; assert!(resp.success, "Deploy error {:?}", resp); let contract = format!("0x{:0>40}", hex::encode(&resp.data)); println!("Contract address: {}", contract); let data = format!( - "{}{:x}", + "{}{:064x}", "1401d2b5", // makeEvent(3232) U256::from(3232) ); - let resp = evm.contract_call(contract.clone(), None, Some(data.clone()), None)?; + let resp = vm.contract_call(contract.clone(), None, Some(data.clone()), None)?; assert!(resp.success, "Call error {:?}", resp); assert!(resp.events.is_empty(), "Expecting no events"); assert!(resp.traces.is_empty(), "Expecting no call traces"); - env::set_var("TINYEVM_CALL_TRACE_ENABLED", "1"); - let resp = evm.contract_call(contract.clone(), None, Some(data), None)?; + vm.set_evm_tracing(true); + let resp = vm.contract_call(contract.clone(), None, Some(data), None)?; assert!(resp.success, "Call error {:?}", resp); assert!(resp.events.len() == 1, "Expecting one event"); diff --git a/tests/test_account_snapshot.py b/tests/test_account_snapshot.py index 27ec533..0d6533d 100644 --- a/tests/test_account_snapshot.py +++ b/tests/test_account_snapshot.py @@ -6,7 +6,6 @@ import json from eth_abi import encode - def handle_exception(exc_type, exc_value, exc_traceback): if issubclass(exc_type, KeyboardInterrupt): sys.__excepthook__(exc_type, exc_value, exc_traceback) @@ -124,7 +123,6 @@ def reset_contract_call(contract, owner): resp = tevm.contract_call(contract, owner, data_balance_check, None) assert _initialSupply - 1000 == int.from_bytes(bytes(resp.data), 'big') - random_address = '0x253397db4016dE1983D29f7DEc2901c54dB81A22' tevm.copy_snapshot(contract, random_address) @@ -140,7 +138,7 @@ def reset_contract_call(contract, owner): -def test_snapshot(benchmark): +def test_account_snapshot(benchmark): owner = '0x9C33eaCc2F50E39940D3AfaF2c7B8246B681A374' contract = deploy_contract(salt='0x1fff0000000000000000000000000000000000000000000000000000000000ff', owner=owner) def reset_contract(): diff --git a/tests/test_fork.py b/tests/test_fork.py index f3f9e78..8de27ed 100644 --- a/tests/test_fork.py +++ b/tests/test_fork.py @@ -14,7 +14,8 @@ def tprint(*args): class TestTinyEVM(unittest.TestCase): @classmethod def setUpClass(cls): - tinyevm.enable_tracing() + # tinyevm.enable_tracing() + pass def test_get_balance_from_fork(self): fork_url = "https://eth.llamarpc.com" diff --git a/tests/test_global_snapshot.py b/tests/test_global_snapshot.py new file mode 100644 index 0000000..07ecb0d --- /dev/null +++ b/tests/test_global_snapshot.py @@ -0,0 +1,185 @@ +import tinyevm +from Crypto.Hash import keccak +import threading +import sys +from datetime import datetime +import json +from eth_abi import encode +from memory_profiler import memory_usage, profile +import gc +import pytest + +# @pytest.fixture +# def benchmark_memory(): +# def benchmark_memory_fn(func, *args, **kwargs): +# mem_usage = memory_usage((func, args, kwargs), interval=0.1, timeout=1) +# return max(mem_usage) - min(mem_usage) +# return benchmark_memory_fn + +def handle_exception(exc_type, exc_value, exc_traceback): + if issubclass(exc_type, KeyboardInterrupt): + sys.__excepthook__(exc_type, exc_value, exc_traceback) + return + + print("Uncaught exception", exc_type, exc_value) + sys.exit(1) + +sys.excepthook = handle_exception + + +def fn_sig(sig): + k = keccak.new(digest_bits=256) + k.update(sig.encode()) + return k.hexdigest()[:8] + +def tprint(*args): + # print(datetime.now(), threading.current_thread().name, *args) + pass + +def encode_balance_of(address): + # Compute the method id for the balanceOf function + method_id = "0x70a08231" # function signature for balanceOf(address) + + # ABI encode the address + address_param = encode('address', address).hex() + + # Concatenate the method id and the address parameter + data = method_id + address_param + + return data + +tevm = tinyevm.TinyEVM() + +def transfer_1000(contract, sender): + fn = fn_sig('transfer(address,uint256)') + data = encode(['address', 'uint256'], ['0x44Eadb1b1288F4883F2166846800335bfFa290be', 1000]).hex() + resp = tevm.contract_call(contract, sender, fn + data, None) + assert resp.success + + +def deploy_contract(salt=None, owner='0x388C818CA8B9251b393131C08a736A67ccB19297'): + """ + Deploy and check the balance of the contract + """ + # generated with + # solc --combined-json abi,bin,bin-runtime,srcmap,srcmap-runtime tests/contracts/snapshots.sol > tests/contracts/snapshots.compiled.json + with open('tests/contracts/snapshots.compiled.json') as f: + out = json.load(f) + + binary = out['contracts']['tests/contracts/snapshots.sol:Test']['bin'] + + tevm.set_balance(owner, 0xffff0000000000000000000000000000000000000000000000000000000000ff) + + salt = None + data = '' + value = None + init_value = None + resp = tevm.deterministic_deploy(binary, salt, owner, data, value, init_value) + tprint('Deployment resp: {}'.format(resp)) + + assert resp.success + + contract = bytes(resp.data).hex() + tprint('Contract deployed to: {}'.format(contract or 'EMPTY')) + + return contract + + +def run_global_snapshot(keep_snapshot: bool, x_fns: list, disable_snapshot=False, take_snapshot_after_each_tx=False): + owner = '0x1e209e340405D4211a3185f97628179917883505' + init_count_value = 1 + + # deploy some contracts to populate a few more acounts + deploy_contract(salt='0x1fff00000000000000000000000000000000000000000000000000000000eeff') + deploy_contract(salt='0x1fff000000000000000000000000000000000000000000000000000000cceeff') + deploy_contract(salt='0x1fff000000000000000000000000000000000000000000000000000000aaeeff') + + # deploy and take a global snapshot + contract = deploy_contract(owner=owner) + snapshot_id = None + if not disable_snapshot: + snapshot_id = tevm.take_global_snapshot() + + # make sure we've deployed successfully by checking the initial states + call_data = fn_sig('counter()') + resp = tevm.contract_call(contract, None, call_data, None) + tprint(f"counter() resp: {resp.data}") + assert resp.success + count = int.from_bytes(bytes(resp.data), 'big') + assert count == init_count_value + + # make some transactions + for (fn, expected_return_value) in x_fns: + call_data = fn_sig(fn) + resp = tevm.contract_call(contract, owner, call_data, None) + assert resp.success + count = int.from_bytes(bytes(resp.data), 'big') + if expected_return_value is not None: + assert count == expected_return_value + if take_snapshot_after_each_tx and not disable_snapshot: + tevm.take_global_snapshot() + + # restore the snapshot + if not disable_snapshot: + for _ in range(1000): + tevm.restore_global_snapshot(snapshot_id, keep_snapshot) + + # check the global state reverted to previous state + call_data = fn_sig('counter()') + resp = tevm.contract_call(contract, owner, call_data, None) + assert resp.success + count = int.from_bytes(bytes(resp.data), 'big') + if not disable_snapshot: + assert count == init_count_value + + + +x_fns_fastseq = [('fast_seq()', None),] * 1000 + +x_fns_slowseq = [('slow_seq()', None), ] * 100 + +@pytest.mark.parametrize("x_fns, disable_snapshot, name", [ + (x_fns_fastseq, False, "fastseq, plus take and restore snapshot"), + (x_fns_slowseq, False, "slowseq, plus take and restore snapshot"), + (x_fns_fastseq, True, "fastseq"), + (x_fns_slowseq, True, "slowseq"), +]) +def test_global_snapshot(benchmark, x_fns, disable_snapshot, name): + global tevm + tevm = tinyevm.TinyEVM() + def run(): + run_global_snapshot(True, x_fns, disable_snapshot=disable_snapshot) + benchmark(run) + + + +@profile +def test_compare_memory(): + x_fns_fastseq = [('fast_seq()', 1 + 5 * i) for i in range(1, 1000)] + x_fns_slowseq = [('slow_seq()', 1 + 5 * 50 * i) for i in range(1, 100)] + global tevm + + + # Running 100 slowseq without taking snapshots + tevm = tinyevm.TinyEVM() + gc.collect() + run_global_snapshot(True, x_fns_slowseq, disable_snapshot=True) + gc.collect() + + # Running 1000 fastseq without taking snapshots + tevm = tinyevm.TinyEVM() + gc.collect() + run_global_snapshot(True, x_fns_fastseq, disable_snapshot=True) + gc.collect() + + # Running slowseq taking 100 snapshots + tevm = tinyevm.TinyEVM() + gc.collect() + run_global_snapshot(True, x_fns_slowseq, take_snapshot_after_each_tx=True) + gc.collect() + + # Running fastseq taking 1000 snapshots + tevm = tinyevm.TinyEVM() + gc.collect() + run_global_snapshot(True, x_fns_fastseq, take_snapshot_after_each_tx=True) + gc.collect() diff --git a/tests/test_multithreading.py b/tests/test_multithreading.py index 5d17b2a..33817c4 100644 --- a/tests/test_multithreading.py +++ b/tests/test_multithreading.py @@ -7,6 +7,8 @@ from concurrent.futures import ProcessPoolExecutor +salt = None + def handle_exception(exc_type, exc_value, exc_traceback): if issubclass(exc_type, KeyboardInterrupt): sys.__excepthook__(exc_type, exc_value, exc_traceback) @@ -33,7 +35,6 @@ def run_test(): tevm = tinyevm.TinyEVM() contract_bytecode = open('tests/contracts/C.hex').read() - salt = None owner = '0x388C818CA8B9251b393131C08a736A67ccB19297' data = None value = None @@ -43,7 +44,6 @@ def run_test(): balance = tevm.get_balance(owner) tprint('balance before deployment: {}'.format(balance)) - # todo update response object resp = tevm.deterministic_deploy(contract_bytecode, salt, owner, data, value, init_value) tprint('Deployment resp: {}'.format(resp)) @@ -75,7 +75,6 @@ def run_infinite_loop(): tevm = tinyevm.TinyEVM() contract_bytecode = open('tests/contracts/infinite_loop_Test.hex').read() - salt = None owner = '0x388C818CA8B9251b393131C08a736A67ccB19297' data = None value = None diff --git a/tests/test_tinyevm.py b/tests/test_tinyevm.py index 38ae392..268824f 100644 --- a/tests/test_tinyevm.py +++ b/tests/test_tinyevm.py @@ -2,6 +2,8 @@ import unittest from Crypto.Hash import keccak +salt = None + def fn_sig(sig): k = keccak.new(digest_bits=256) k.update(sig.encode()) @@ -35,9 +37,9 @@ def test_get_set_env_field(self): tevm = tinyevm.TinyEVM() # block_number - assert tevm.get_env_value_by_field('block_number') == '0x0000000000000000000000000000000000000000000000000000000000000000' + assert int(tevm.get_env_value_by_field('block_number'), 16) == int('0x0000000000000000000000000000000000000000000000000000000000000000', 16) tevm.set_env_field_value('block_number', '0x00000000000000000000000000000000000000000000000000000000000000ff') - assert tevm.get_env_value_by_field('block_number') == '0x00000000000000000000000000000000000000000000000000000000000000ff' + assert int(tevm.get_env_value_by_field('block_number'), 16) == int('0x00000000000000000000000000000000000000000000000000000000000000ff', 16) # origin assert tevm.get_env_value_by_field('origin') == '0x0000000000000000000000000000000000000000' @@ -46,11 +48,10 @@ def test_get_set_env_field(self): def test_get_change_tx_gas_limit(self): tevm = tinyevm.TinyEVM() - tevm.tx_gas_limit = 100 + tevm.tx_gas_limit = 0xffffffffffff - assert tevm.tx_gas_limit == 100, 'default_tx_gas_limit should be changed to 100' + assert tevm.tx_gas_limit == 0xffffffffffff, 'default_tx_gas_limit should be changed to 100' contract_bytecode = open('tests/contracts/C.hex').read() - salt = None owner = '0x388C818CA8B9251b393131C08a736A67ccB19297' data = None value = None @@ -80,7 +81,6 @@ def test_deployment(self): tevm = tinyevm.TinyEVM() contract_bytecode = open('tests/contracts/C.hex').read() - salt = None owner = '0x388C818CA8B9251b393131C08a736A67ccB19297' data = None value = None