diff --git a/.github/actions/rust-cargo-run/action.yml b/.github/actions/rust-cargo-run/action.yml index ee2aa8cff..9bee5bfa8 100644 --- a/.github/actions/rust-cargo-run/action.yml +++ b/.github/actions/rust-cargo-run/action.yml @@ -20,7 +20,7 @@ inputs: cache_name: description: The name of the cache to save/restore required: true - default: v2-test + default: test runs: using: composite @@ -41,7 +41,7 @@ runs: CACHE_SKIP_SAVE: ${{ inputs.save_cache == '' || inputs.save_cache == 'false' }} with: version: v0.2.15 - shared-key: ${{ inputs.cache_name }} # change this to invalidate sccache for this job + shared-key: v5-${{ inputs.cache_name }} # change this to invalidate sccache for this job - name: Running ${{ inputs.command }} uses: actions-rs/cargo@844f36862e911db73fe0815f00a4a2602c279505 # v1.0.3 env: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e36cb97a6..c1304439e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,6 +4,7 @@ on: push: branches: - master + - next pull_request: env: @@ -43,7 +44,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - network: [ 'mainnet', 'caterpillarnet', 'butterflynet', 'calibrationnet', 'devnet', 'testing', 'testing-fake-proofs' ] + network: [ 'mainnet', 'caterpillarnet', 'butterflynet', 'calibrationnet', 'devnet', 'testing', 'testing-fake-proofs', 'wallaby', 'hyperspace', 'devnet-wasm' ] steps: - name: Checking out uses: actions/checkout@v2 @@ -57,6 +58,10 @@ jobs: BUILD_FIL_NETWORK: ${{ matrix.network }} run: | cargo run --locked -- -o output/builtin-actors.car + - name: Checking no wasm-bindgen references + run: | + sudo apt-get update && sudo apt-get -y install wabt parallel + find ./target -name '*.wasm' -print0 | parallel -0 --halt now,fail=1 sh -c 'wasm2wat --enable-bulk-memory {} | grep bindgen; test $? -ne 0' coverage: runs-on: ubuntu-latest env: @@ -70,7 +75,7 @@ jobs: command: version components: llvm-tools-preview github_token: ${{ secrets.GITHUB_TOKEN }} - cache_name: v3-cov + cache_name: cov save_cache: true - name: Put LLVM tools into the PATH run: echo "${HOME}/.rustup/toolchains/$(cat rust-toolchain)-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/bin" >> $GITHUB_PATH diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 36326fcc7..9b5824d99 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,7 +16,7 @@ jobs: CACHE_SKIP_SAVE: ${{ matrix.push == '' || matrix.push == 'false' }} strategy: matrix: - network: [ 'mainnet', 'caterpillarnet', 'butterflynet', 'calibrationnet', 'devnet', 'testing', 'testing-fake-proofs' ] + network: [ 'mainnet', 'caterpillarnet', 'butterflynet', 'calibrationnet', 'devnet', 'testing', 'testing-fake-proofs', 'wallaby', 'hyperspace', 'devnet-wasm' ] steps: - name: Checking out uses: actions/checkout@v2 diff --git a/Cargo.lock b/Cargo.lock index 11c7d2a5c..834044bc7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,50 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static", + "regex", +] + +[[package]] +name = "aes" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +dependencies = [ + "cfg-if", + "cipher 0.3.0", + "cpufeatures", + "opaque-debug 0.3.0", +] + +[[package]] +name = "aes" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241" +dependencies = [ + "cfg-if", + "cipher 0.4.3", + "cpufeatures", +] + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + [[package]] name = "aho-corasick" version = "0.7.20" @@ -142,6 +186,17 @@ dependencies = [ "syn", ] +[[package]] +name = "async_io_stream" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d7b9decdf35d8908a7e3ef02f64c5e9b1695e230154c0e8de3969142d9b94c" +dependencies = [ + "futures", + "pharos", + "rustc_version", +] + [[package]] name = "atomic-waker" version = "1.1.0" @@ -154,11 +209,35 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] +[[package]] +name = "auto_impl" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7862e21c893d65a1650125d157eaeec691439379a1cee17ee49031b79236ada4" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "auto_impl" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a8c1df849285fbacd587de7818cc7d13be6cd2cbcd47a04fb1801b0e2706e33" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -171,24 +250,110 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + +[[package]] +name = "base58" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5024ee8015f02155eee35c711107ddd9a9bf3cb689cf2a9089c97e79b6e1ae83" + +[[package]] +name = "base58check" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ee2fe4c9a0c84515f136aaae2466744a721af6d63339c18689d9e995d74d99b" +dependencies = [ + "base58", + "sha2 0.8.2", +] + +[[package]] +name = "base64" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" + [[package]] name = "base64" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + +[[package]] +name = "base64ct" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a32fd6af2b5827bce66c29053ba0e7c42b9dcab01835835058558c10851a46b" + +[[package]] +name = "bech32" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dabbe35f96fb9507f7330793dc490461b2962659ac5d427181e451a623751d1" + [[package]] name = "bimap" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc0455254eb5c6964c4545d8bac815e1a1be4f3afe0ae695ea539c12d728d44b" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitvec" +version = "0.17.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41262f11d771fd4a61aa3ce019fca363b4b6c282fca9da2a31186d3965a47a5c" +dependencies = [ + "either", + "radium 0.3.0", +] + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium 0.7.0", + "tap", + "wyz", +] + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest 0.10.6", +] + [[package]] name = "blake2b_simd" version = "1.0.0" @@ -224,13 +389,25 @@ dependencies = [ "constant_time_eq 0.2.4", ] +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding", + "byte-tools", + "byteorder", + "generic-array 0.12.4", +] + [[package]] name = "block-buffer" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "generic-array", + "generic-array 0.14.6", ] [[package]] @@ -239,7 +416,16 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" dependencies = [ - "generic-array", + "generic-array 0.14.6", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", ] [[package]] @@ -256,12 +442,96 @@ dependencies = [ "futures-lite", ] +[[package]] +name = "borsh" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15bf3650200d8bffa99015595e10f1fbd17de07abbc25bb067da79e769939bfa" +dependencies = [ + "borsh-derive", + "hashbrown 0.11.2", +] + +[[package]] +name = "borsh-derive" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6441c552f230375d18e3cc377677914d2ca2b0d36e52129fe15450a2dce46775" +dependencies = [ + "borsh-derive-internal", + "borsh-schema-derive-internal", + "proc-macro-crate 0.1.5", + "proc-macro2", + "syn", +] + +[[package]] +name = "borsh-derive-internal" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "borsh-schema-derive-internal" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" + [[package]] name = "bumpalo" version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + +[[package]] +name = "bytecheck" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d11cac2c12b5adc6570dad2ee1b87eff4955dac476fe12d81e5fdd352e52406f" +dependencies = [ + "bytecheck_derive", + "ptr_meta", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13e576ebe98e605500b3c8041bb888e966653577172df6dd97398714eb30b9bf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "byteorder" version = "1.4.3" @@ -270,9 +540,44 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.4.0" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" +dependencies = [ + "serde", +] + +[[package]] +name = "camino" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c77df041dc383319cc661b428b6961a005db4d6808d5e12536931b1ca9556055" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "982a0cf6a99c350d7246035613882e376d58cebe571785abc5da4f648d53ac0a" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror", +] [[package]] name = "castaway" @@ -304,6 +609,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +dependencies = [ + "num-integer", + "num-traits", +] + [[package]] name = "cid" version = "0.8.6" @@ -318,6 +633,25 @@ dependencies = [ "unsigned-varint", ] +[[package]] +name = "cipher" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +dependencies = [ + "generic-array 0.14.6", +] + +[[package]] +name = "cipher" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1873270f8f7942c191139cb8a40fd228da6c3fd2fc376d7e92d47aa14aeb59e" +dependencies = [ + "crypto-common", + "inout", +] + [[package]] name = "clap" version = "3.2.23" @@ -358,38 +692,116 @@ dependencies = [ ] [[package]] -name = "concurrent-queue" -version = "2.1.0" +name = "coins-bip32" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c278839b831783b70278b14df4d45e1beb1aad306c07bb796637de9a0e323e8e" +checksum = "634c509653de24b439672164bbf56f5f582a2ab0e313d3b0f6af0b7345cf2560" dependencies = [ - "crossbeam-utils", + "bincode", + "bs58", + "coins-core", + "digest 0.10.6", + "getrandom", + "hmac 0.12.1", + "k256", + "lazy_static", + "serde", + "sha2 0.10.6", + "thiserror", ] [[package]] -name = "constant_time_eq" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" - -[[package]] -name = "constant_time_eq" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3ad85c1f65dc7b37604eb0e89748faf0b9653065f2a8ef69f96a687ec1e9279" - -[[package]] -name = "core2" -version = "0.4.0" +name = "coins-bip39" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +checksum = "2a11892bcac83b4c6e95ab84b5b06c76d9d70ad73548dd07418269c5c7977171" dependencies = [ - "memchr", + "bitvec 0.17.4", + "coins-bip32", + "getrandom", + "hex", + "hmac 0.12.1", + "pbkdf2 0.11.0", + "rand", + "sha2 0.10.6", + "thiserror", ] [[package]] -name = "cpufeatures" -version = "0.2.5" +name = "coins-core" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c94090a6663f224feae66ab01e41a2555a8296ee07b5f20dab8888bdefc9f617" +dependencies = [ + "base58check", + "base64 0.12.3", + "bech32", + "blake2", + "digest 0.10.6", + "generic-array 0.14.6", + "hex", + "ripemd", + "serde", + "serde_derive", + "sha2 0.10.6", + "sha3", + "thiserror", +] + +[[package]] +name = "concurrent-queue" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c278839b831783b70278b14df4d45e1beb1aad306c07bb796637de9a0e323e8e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const-oid" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cec318a675afcb6a1ea1d4340e2d377e56e47c266f28043ceccbf4412ddfdd3b" + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "constant_time_eq" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3ad85c1f65dc7b37604eb0e89748faf0b9653065f2a8ef69f96a687ec1e9279" + +[[package]] +name = "convert_case" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb4a24b1aaf0fd0ce8b45161144d6f42cd91677fd5940fd431183eb023b3a2b8" + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + +[[package]] +name = "cpufeatures" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" dependencies = [ @@ -411,16 +823,38 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array 0.14.6", + "rand_core", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "generic-array", + "generic-array 0.14.6", "typenum", ] +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array 0.14.6", + "subtle", +] + [[package]] name = "cs_serde_bytes" version = "0.12.2" @@ -440,6 +874,24 @@ dependencies = [ "syn", ] +[[package]] +name = "ctr" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" +dependencies = [ + "cipher 0.3.0", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher 0.4.3", +] + [[package]] name = "darling" version = "0.12.4" @@ -501,6 +953,16 @@ dependencies = [ "syn", ] +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", + "zeroize", +] + [[package]] name = "derive_builder" version = "0.10.2" @@ -532,13 +994,33 @@ dependencies = [ "syn", ] +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array 0.12.4", +] + [[package]] name = "digest" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array", + "generic-array 0.14.6", ] [[package]] @@ -549,6 +1031,31 @@ checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ "block-buffer 0.10.3", "crypto-common", + "subtle", +] + +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + +[[package]] +name = "dunce" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bd4b30a6560bbd9b4620f4de34c3f14f60848e58a9b7216801afcb4c7b31c3c" + +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der", + "elliptic-curve", + "rfc6979", + "signature", ] [[package]] @@ -557,12 +1064,645 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct", + "crypto-bigint", + "der", + "digest 0.10.6", + "ff", + "generic-array 0.14.6", + "group", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "encoding_rs" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "env_logger" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "eth-keystore" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f65b750ac950f2f825b36d08bef4cda4112e19a7b1a68f6e2bb499413e12440" +dependencies = [ + "aes 0.7.5", + "ctr 0.8.0", + "digest 0.10.6", + "hex", + "hmac 0.12.1", + "pbkdf2 0.11.0", + "rand", + "scrypt 0.8.1", + "serde", + "serde_json", + "sha2 0.10.6", + "sha3", + "thiserror", + "uuid", +] + +[[package]] +name = "eth-keystore" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fda3bf123be441da5260717e0661c25a2fd9cb2b2c1d20bf2e05580047158ab" +dependencies = [ + "aes 0.8.2", + "ctr 0.9.2", + "digest 0.10.6", + "hex", + "hmac 0.12.1", + "pbkdf2 0.11.0", + "rand", + "scrypt 0.10.0", + "serde", + "serde_json", + "sha2 0.10.6", + "sha3", + "thiserror", + "uuid", +] + +[[package]] +name = "ethabi" +version = "17.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4966fba78396ff92db3b817ee71143eccd98acf0f876b8d600e585a670c5d1b" +dependencies = [ + "ethereum-types 0.13.1", + "hex", + "once_cell", + "regex", + "serde", + "serde_json", + "sha3", + "thiserror", + "uint", +] + +[[package]] +name = "ethabi" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7413c5f74cc903ea37386a8965a936cbeb334bd270862fdece542c1b2dcbc898" +dependencies = [ + "ethereum-types 0.14.1", + "hex", + "once_cell", + "regex", + "serde", + "serde_json", + "sha3", + "thiserror", + "uint", +] + +[[package]] +name = "ethbloom" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11da94e443c60508eb62cf256243a64da87304c2802ac2528847f79d750007ef" +dependencies = [ + "crunchy", + "fixed-hash 0.7.0", + "impl-rlp", + "impl-serde 0.3.2", + "tiny-keccak", +] + +[[package]] +name = "ethbloom" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" +dependencies = [ + "crunchy", + "fixed-hash 0.8.0", + "impl-codec", + "impl-rlp", + "impl-serde 0.4.0", + "scale-info", + "tiny-keccak", +] + +[[package]] +name = "ethereum-types" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2827b94c556145446fcce834ca86b7abf0c39a805883fe20e72c5bfdb5a0dc6" +dependencies = [ + "ethbloom 0.12.1", + "fixed-hash 0.7.0", + "impl-rlp", + "impl-serde 0.3.2", + "primitive-types 0.11.1", + "uint", +] + +[[package]] +name = "ethereum-types" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" +dependencies = [ + "ethbloom 0.13.0", + "fixed-hash 0.8.0", + "impl-codec", + "impl-rlp", + "impl-serde 0.4.0", + "primitive-types 0.12.1", + "scale-info", + "uint", +] + +[[package]] +name = "ethers" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16142eeb3155cfa5aec6be3f828a28513a28bd995534f945fa70e7d608f16c10" +dependencies = [ + "ethers-addressbook 0.17.0", + "ethers-contract 0.17.0", + "ethers-core 0.17.0", + "ethers-etherscan 0.17.0", + "ethers-middleware 0.17.0", + "ethers-providers 0.17.0", + "ethers-signers 0.17.0", +] + +[[package]] +name = "ethers" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11f26f9d8d80da18ca72aca51804c65eb2153093af3bec74fd5ce32aa0c1f665" +dependencies = [ + "ethers-addressbook 1.0.2", + "ethers-contract 1.0.2", + "ethers-core 1.0.2", + "ethers-etherscan 1.0.2", + "ethers-middleware 1.0.2", + "ethers-providers 1.0.2", + "ethers-signers 1.0.2", +] + +[[package]] +name = "ethers-addressbook" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e23f8992ecf45ea9dd2983696aabc566c108723585f07f5dc8c9efb24e52d3db" +dependencies = [ + "ethers-core 0.17.0", + "once_cell", + "serde", + "serde_json", +] + +[[package]] +name = "ethers-addressbook" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4be54dd2260945d784e06ccdeb5ad573e8f1541838cee13a1ab885485eaa0b" +dependencies = [ + "ethers-core 1.0.2", + "once_cell", + "serde", + "serde_json", +] + +[[package]] +name = "ethers-contract" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e0010fffc97c5abcf75a30fd75676b1ed917b2b82beb8270391333618e2847d" +dependencies = [ + "ethers-contract-abigen 0.17.0", + "ethers-contract-derive 0.17.0", + "ethers-core 0.17.0", + "ethers-providers 0.17.0", + "futures-util", + "hex", + "once_cell", + "pin-project", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "ethers-contract" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9c3c3e119a89f0a9a1e539e7faecea815f74ddcf7c90d0b00d1f524db2fdc9c" +dependencies = [ + "ethers-contract-abigen 1.0.2", + "ethers-contract-derive 1.0.2", + "ethers-core 1.0.2", + "ethers-providers 1.0.2", + "futures-util", + "hex", + "once_cell", + "pin-project", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "ethers-contract-abigen" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda76ce804d524f693a898dc5857d08f4db443f3da64d0c36237fa05c0ecef30" +dependencies = [ + "Inflector", + "cfg-if", + "dunce", + "ethers-core 0.17.0", + "eyre", + "getrandom", + "hex", + "proc-macro2", + "quote", + "reqwest", + "serde", + "serde_json", + "syn", + "url", + "walkdir", +] + +[[package]] +name = "ethers-contract-abigen" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d4e5ad46aede34901f71afdb7bb555710ed9613d88d644245c657dc371aa228" +dependencies = [ + "Inflector", + "cfg-if", + "dunce", + "ethers-core 1.0.2", + "eyre", + "getrandom", + "hex", + "proc-macro2", + "quote", + "regex", + "reqwest", + "serde", + "serde_json", + "syn", + "toml", + "url", + "walkdir", +] + +[[package]] +name = "ethers-contract-derive" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41170ccb5950f559cba5a052158a28ec2d224af3a7d5b266b3278b929538ef55" +dependencies = [ + "ethers-contract-abigen 0.17.0", + "ethers-core 0.17.0", + "hex", + "proc-macro2", + "quote", + "serde_json", + "syn", +] + +[[package]] +name = "ethers-contract-derive" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f192e8e4cf2b038318aae01e94e7644e0659a76219e94bcd3203df744341d61f" +dependencies = [ + "ethers-contract-abigen 1.0.2", + "ethers-core 1.0.2", + "hex", + "proc-macro2", + "quote", + "serde_json", + "syn", +] + +[[package]] +name = "ethers-core" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ebdd63c828f58aa067f40f9adcbea5e114fb1f90144b3a1e2858e0c9b1ff4e8" +dependencies = [ + "arrayvec", + "bytes", + "cargo_metadata", + "chrono", + "convert_case 0.5.0", + "elliptic-curve", + "ethabi 17.2.0", + "fastrlp", + "generic-array 0.14.6", + "hex", + "k256", + "once_cell", + "proc-macro2", + "rand", + "rlp", + "rlp-derive", + "rust_decimal", + "serde", + "serde_json", + "strum", + "syn", + "thiserror", + "tiny-keccak", + "unicode-xid", +] + +[[package]] +name = "ethers-core" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade3e9c97727343984e1ceada4fdab11142d2ee3472d2c67027d56b1251d4f15" +dependencies = [ + "arrayvec", + "bytes", + "cargo_metadata", + "chrono", + "convert_case 0.6.0", + "elliptic-curve", + "ethabi 18.0.0", + "generic-array 0.14.6", + "hex", + "k256", + "once_cell", + "open-fastrlp", + "proc-macro2", + "rand", + "rlp", + "rlp-derive", + "serde", + "serde_json", + "strum", + "syn", + "thiserror", + "tiny-keccak", + "unicode-xid", +] + +[[package]] +name = "ethers-etherscan" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b279a3d00bd219caa2f9a34451b4accbfa9a1eaafc26dcda9d572591528435f0" +dependencies = [ + "ethers-core 0.17.0", + "getrandom", + "reqwest", + "semver", + "serde", + "serde-aux 3.1.0", + "serde_json", + "thiserror", + "tracing", +] + +[[package]] +name = "ethers-etherscan" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9713f525348e5dde025d09b0a4217429f8074e8ff22c886263cc191e87d8216" +dependencies = [ + "ethers-core 1.0.2", + "getrandom", + "reqwest", + "semver", + "serde", + "serde-aux 4.1.2", + "serde_json", + "thiserror", + "tracing", +] + +[[package]] +name = "ethers-middleware" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e7e8632d28175352b9454bbcb604643b6ca1de4d36dc99b3f86860d75c132b" +dependencies = [ + "async-trait", + "ethers-contract 0.17.0", + "ethers-core 0.17.0", + "ethers-etherscan 0.17.0", + "ethers-providers 0.17.0", + "ethers-signers 0.17.0", + "futures-locks", + "futures-util", + "instant", + "reqwest", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", + "tracing-futures", + "url", +] + +[[package]] +name = "ethers-middleware" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e71df7391b0a9a51208ffb5c7f2d068900e99d6b3128d3a4849d138f194778b7" +dependencies = [ + "async-trait", + "auto_impl 0.5.0", + "ethers-contract 1.0.2", + "ethers-core 1.0.2", + "ethers-etherscan 1.0.2", + "ethers-providers 1.0.2", + "ethers-signers 1.0.2", + "futures-locks", + "futures-util", + "instant", + "reqwest", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", + "tracing-futures", + "url", +] + +[[package]] +name = "ethers-providers" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e46482e4d1e79b20c338fd9db9e166184eb387f0a4e7c05c5b5c0aa2e8c8900c" +dependencies = [ + "async-trait", + "auto_impl 1.0.1", + "base64 0.13.1", + "ethers-core 0.17.0", + "futures-core", + "futures-timer", + "futures-util", + "getrandom", + "hashers", + "hex", + "http", + "once_cell", + "parking_lot", + "pin-project", + "reqwest", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", + "tracing-futures", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-timer", + "web-sys", + "ws_stream_wasm", +] + +[[package]] +name = "ethers-providers" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a9e0597aa6b2fdc810ff58bc95e4eeaa2c219b3e615ed025106ecb027407d8" +dependencies = [ + "async-trait", + "auto_impl 1.0.1", + "base64 0.13.1", + "ethers-core 1.0.2", + "futures-core", + "futures-timer", + "futures-util", + "getrandom", + "hashers", + "hex", + "http", + "once_cell", + "parking_lot", + "pin-project", + "reqwest", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", + "tracing-futures", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-timer", + "web-sys", + "ws_stream_wasm", +] + +[[package]] +name = "ethers-signers" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73a72ecad124e8ccd18d6a43624208cab0199e59621b1f0fa6b776b2e0529107" +dependencies = [ + "async-trait", + "coins-bip32", + "coins-bip39", + "elliptic-curve", + "eth-keystore 0.4.2", + "ethers-core 0.17.0", + "hex", + "rand", + "sha2 0.10.6", + "thiserror", +] + +[[package]] +name = "ethers-signers" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f41ced186867f64773db2e55ffdd92959e094072a1d09a5e5e831d443204f98" +dependencies = [ + "async-trait", + "coins-bip32", + "coins-bip39", + "elliptic-curve", + "eth-keystore 0.5.0", + "ethers-core 1.0.2", + "hex", + "rand", + "sha2 0.10.6", + "thiserror", +] + +[[package]] +name = "etk-asm" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f930978593dfe8eb61901b68b083f5b08898f45ed7387ca627ede2f036b426a" +dependencies = [ + "hex", + "num-bigint", + "pest", + "pest_derive", + "rand", + "sha3", + "snafu", +] + [[package]] name = "event-listener" version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +[[package]] +name = "eyre" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + [[package]] name = "fastrand" version = "1.8.0" @@ -572,6 +1712,41 @@ dependencies = [ "instant", ] +[[package]] +name = "fastrlp" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "089263294bb1c38ac73649a6ad563dd9a5142c8dc0482be15b8b9acb22a1611e" +dependencies = [ + "arrayvec", + "auto_impl 1.0.1", + "bytes", + "ethereum-types 0.13.1", + "fastrlp-derive", +] + +[[package]] +name = "fastrlp-derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6e454d03710df0cd95ce075d7731ce3fa35fb3779c15270cd491bc5f2ef9355" +dependencies = [ + "bytes", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core", + "subtle", +] + [[package]] name = "fil_actor_account" version = "10.0.0-alpha.1" @@ -682,10 +1857,30 @@ dependencies = [ name = "fil_actor_evm" version = "10.0.0-alpha.1" dependencies = [ + "anyhow", + "cid", + "ethers 1.0.2", + "etk-asm", "fil_actors_evm_shared", "fil_actors_runtime", + "frc42_dispatch", + "fvm_ipld_blockstore", "fvm_ipld_encoding 0.3.3", + "fvm_ipld_kamt", + "fvm_shared 3.0.0-alpha.20", + "hex", + "hex-literal", + "lazy_static", + "log", + "multihash", + "num-derive", + "num-traits", + "once_cell", + "rand", "serde", + "serde_json", + "serde_tuple", + "substrate-bn", ] [[package]] @@ -886,9 +2081,12 @@ dependencies = [ name = "fil_actors_evm_shared" version = "10.0.0-alpha.1" dependencies = [ + "fil_actors_runtime", "fvm_ipld_encoding 0.3.3", + "fvm_shared 3.0.0-alpha.20", "hex", "serde", + "uint", ] [[package]] @@ -896,7 +2094,6 @@ name = "fil_actors_runtime" version = "10.0.0-alpha.1" dependencies = [ "anyhow", - "base64", "blake2b_simd", "byteorder", "castaway", @@ -909,10 +2106,7 @@ dependencies = [ "fvm_ipld_hamt", "fvm_sdk 3.0.0-alpha.24", "fvm_shared 3.0.0-alpha.20", - "getrandom", "hex", - "indexmap", - "integer-encoding", "itertools", "lazy_static", "libsecp256k1", @@ -922,6 +2116,7 @@ dependencies = [ "num-derive", "num-traits", "paste", + "pretty_env_logger", "rand", "regex", "serde", @@ -941,6 +2136,7 @@ dependencies = [ "fil_actor_bundler", "fil_actor_cron", "fil_actor_datacap", + "fil_actor_eam", "fil_actor_ethaccount", "fil_actor_evm", "fil_actor_init", @@ -986,6 +2182,30 @@ dependencies = [ "serde", ] +[[package]] +name = "fixed-hash" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" +dependencies = [ + "byteorder", + "rand", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand", + "rustc-hex", + "static_assertions", +] + [[package]] name = "fnv" version = "1.0.7" @@ -1002,6 +2222,15 @@ dependencies = [ "serde", ] +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + [[package]] name = "frc42_dispatch" version = "3.0.1-alpha.2" @@ -1063,6 +2292,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futures" version = "0.3.25" @@ -1126,6 +2361,17 @@ dependencies = [ "waker-fn", ] +[[package]] +name = "futures-locks" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45ec6fe3675af967e67c5536c0b9d44e34e6c52f86bedc4ea49c5317b8e94d06" +dependencies = [ + "futures-channel", + "futures-task", + "tokio", +] + [[package]] name = "futures-macro" version = "0.3.25" @@ -1149,6 +2395,12 @@ version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" +[[package]] +name = "futures-timer" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" + [[package]] name = "futures-util" version = "0.3.25" @@ -1189,8 +2441,6 @@ dependencies = [ [[package]] name = "fvm_ipld_amt" version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e84f16d6927ce342ef86bd20fcc2d5bd498ed33ae6d7a22fea7a1b453488ec88" dependencies = [ "anyhow", "cid", @@ -1205,8 +2455,6 @@ dependencies = [ [[package]] name = "fvm_ipld_bitfield" version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1950291f40d2d1047eb0a4568f7ef6d5b4973452dcef012dffb1957fe483ff7" dependencies = [ "fvm_ipld_encoding 0.3.3", "serde", @@ -1217,8 +2465,6 @@ dependencies = [ [[package]] name = "fvm_ipld_blockstore" version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "688239a96199577f6705a3f9689abfd795f867f91f5847bc7e236017cc672df7" dependencies = [ "anyhow", "cid", @@ -1261,8 +2507,6 @@ dependencies = [ [[package]] name = "fvm_ipld_encoding" version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0816a2a6df4853de08a723d261110d56a121aa313bc570fe9d248f0a4bc5288" dependencies = [ "anyhow", "cid", @@ -1278,8 +2522,6 @@ dependencies = [ [[package]] name = "fvm_ipld_hamt" version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c942494dde990aeac314311bde34c787be99cab7d0836397a75556cbaa2c3e7" dependencies = [ "anyhow", "byteorder", @@ -1295,6 +2537,23 @@ dependencies = [ "thiserror", ] +[[package]] +name = "fvm_ipld_kamt" +version = "0.2.0" +dependencies = [ + "anyhow", + "byteorder", + "cid", + "forest_hash_utils", + "fvm_ipld_blockstore", + "fvm_ipld_encoding 0.3.3", + "multihash", + "once_cell", + "serde", + "sha2 0.10.6", + "thiserror", +] + [[package]] name = "fvm_sdk" version = "2.2.0" @@ -1313,8 +2572,6 @@ dependencies = [ [[package]] name = "fvm_sdk" version = "3.0.0-alpha.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad163015237811580399ce929938f3bdca2d1a133672ff01693f555a632ec425" dependencies = [ "cid", "fvm_ipld_encoding 0.3.3", @@ -1357,8 +2614,6 @@ dependencies = [ [[package]] name = "fvm_shared" version = "3.0.0-alpha.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b594b4d4c1393f03e3430f106c09874fcac565e68a560686de37ca2982a61d1a" dependencies = [ "anyhow", "bitflags", @@ -1384,78 +2639,332 @@ dependencies = [ ] [[package]] -name = "generic-array" -version = "0.14.6" +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum", +] + +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "h2" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashers" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2bca93b15ea5a746f220e56587f71e73c6165eab783df9e26590069953e3c30" +dependencies = [ + "fxhash", +] + +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] + +[[package]] +name = "hex-literal" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" + +[[package]] +name = "hmac" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" +dependencies = [ + "crypto-mac", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.6", +] + +[[package]] +name = "hmac-drbg" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" +dependencies = [ + "digest 0.9.0", + "generic-array 0.14.6", + "hmac 0.8.1", +] + +[[package]] +name = "http" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "humantime" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" +dependencies = [ + "quick-error", +] + +[[package]] +name = "hyper" +version = "0.14.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" dependencies = [ - "typenum", - "version_check", + "http", + "hyper", + "rustls", + "tokio", + "tokio-rustls", ] [[package]] -name = "getrandom" -version = "0.2.8" +name = "ident_case" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] -name = "gloo-timers" -version = "0.2.6" +name = "idna" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", + "unicode-bidi", + "unicode-normalization", ] [[package]] -name = "hashbrown" -version = "0.12.3" +name = "impl-codec" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] [[package]] -name = "heck" -version = "0.4.0" +name = "impl-rlp" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" +dependencies = [ + "rlp", +] [[package]] -name = "hermit-abi" -version = "0.1.19" +name = "impl-serde" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +checksum = "4551f042f3438e64dbd6226b20527fc84a6e1fe65688b58746a2f53623f25f5c" dependencies = [ - "libc", + "serde", ] [[package]] -name = "hex" -version = "0.4.3" +name = "impl-serde" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" +dependencies = [ + "serde", +] [[package]] -name = "hex-literal" -version = "0.3.4" +name = "impl-trait-for-tuples" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] -name = "ident_case" -version = "1.0.1" +name = "indenter" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" @@ -1464,10 +2973,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", "serde", ] +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array 0.14.6", +] + [[package]] name = "instant" version = "0.1.12" @@ -1475,6 +2993,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", ] [[package]] @@ -1487,6 +3008,12 @@ dependencies = [ "futures-util", ] +[[package]] +name = "ipnet" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" + [[package]] name = "itertools" version = "0.10.5" @@ -1511,6 +3038,19 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "k256" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "sha2 0.10.6", + "sha3", +] + [[package]] name = "keccak" version = "0.1.3" @@ -1534,6 +3074,9 @@ name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin", +] [[package]] name = "libc" @@ -1578,14 +3121,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" dependencies = [ "arrayref", - "base64", + "base64 0.13.1", "digest 0.9.0", + "hmac-drbg", "libsecp256k1-core", "libsecp256k1-gen-ecmult", "libsecp256k1-gen-genmult", "rand", "serde", "sha2 0.9.9", + "typenum", ] [[package]] @@ -1617,6 +3162,16 @@ dependencies = [ "libsecp256k1-core", ] +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.17" @@ -1633,6 +3188,24 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "mio" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys", +] + [[package]] name = "multibase" version = "0.9.1" @@ -1670,7 +3243,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d6d4752e6230d8ef7adf7bd5d8c4b1f6561c1014c5ba9a37445ccefe18aa1db" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.1.3", "proc-macro-error", "proc-macro2", "quote", @@ -1768,36 +3341,251 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + [[package]] name = "once_cell" version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + [[package]] name = "opaque-debug" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "open-fastrlp" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "786393f80485445794f6043fd3138854dd109cc6c4bd1a6383db304c9ce9b9ce" +dependencies = [ + "arrayvec", + "auto_impl 1.0.1", + "bytes", + "ethereum-types 0.14.1", + "open-fastrlp-derive", +] + +[[package]] +name = "open-fastrlp-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "003b2be5c6c53c1cfeb0a238b8a1c3915cd410feb684457a36c10038f764bb1c" +dependencies = [ + "bytes", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "os_str_bytes" version = "6.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" +[[package]] +name = "parity-scale-codec" +version = "3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ab01d0f889e957861bc65888d5ccbe82c158d0270136ba46820d43837cdf72" +dependencies = [ + "arrayvec", + "bitvec 1.0.1", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b" +dependencies = [ + "proc-macro-crate 1.1.3", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "parking" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "password-hash" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d791538a6dcc1e7cb7fe6f6b58aca40e7f79403c45b2bc274008b5e647af1d8" +dependencies = [ + "base64ct", + "rand_core", + "subtle", +] + +[[package]] +name = "password-hash" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" +dependencies = [ + "base64ct", + "rand_core", + "subtle", +] + [[package]] name = "paste" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" +[[package]] +name = "pbkdf2" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271779f35b581956db91a3e55737327a03aa051e90b1c47aeb189508533adfd7" +dependencies = [ + "digest 0.10.6", +] + +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest 0.10.6", + "hmac 0.12.1", + "password-hash 0.4.2", + "sha2 0.10.6", +] + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "pest" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4257b4a04d91f7e9e6290be5d3da4804dd5784fafde3a497d73eb2b4a158c30a" +dependencies = [ + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241cda393b0cdd65e62e07e12454f1f25d57017dcc514b1514cd3c4645e3a0a6" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46b53634d8c8196302953c74d5352f33d0c512a9499bd2ce468fc9f4128fa27c" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef4f1332a8d4678b41966bb4cc1d0676880e84183a1ecc3f4b69f03e99c7a51" +dependencies = [ + "once_cell", + "pest", + "sha2 0.10.6", +] + +[[package]] +name = "pharos" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9567389417feee6ce15dd6527a8a1ecac205ef62c2932bcf3d9f6fc5b78b414" +dependencies = [ + "futures", + "rustc_version", +] + +[[package]] +name = "pin-project" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pin-project-lite" version = "0.2.9" @@ -1808,7 +3596,17 @@ checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der", + "spki", +] [[package]] name = "polling" @@ -1830,6 +3628,52 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "pretty_env_logger" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d" +dependencies = [ + "env_logger", + "log", +] + +[[package]] +name = "primitive-types" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28720988bff275df1f51b171e1b2a18c30d194c4d2b61defdacecd625a5d94a" +dependencies = [ + "fixed-hash 0.7.0", + "impl-codec", + "impl-rlp", + "impl-serde 0.3.2", + "uint", +] + +[[package]] +name = "primitive-types" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" +dependencies = [ + "fixed-hash 0.8.0", + "impl-codec", + "impl-rlp", + "impl-serde 0.4.0", + "scale-info", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml", +] + [[package]] name = "proc-macro-crate" version = "1.1.3" @@ -1873,6 +3717,32 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + [[package]] name = "quote" version = "1.0.23" @@ -1882,6 +3752,18 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "def50a86306165861203e7f84ecffbbdfdea79f0e51039b33de1e952358c47ac" + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rand" version = "0.8.5" @@ -1912,6 +3794,15 @@ dependencies = [ "getrandom", ] +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + [[package]] name = "regex" version = "1.7.1" @@ -1929,6 +3820,80 @@ version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +[[package]] +name = "rend" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79af64b4b6362ffba04eef3a4e10829718a4896dac19daa741851c86781edf95" +dependencies = [ + "bytecheck", +] + +[[package]] +name = "reqwest" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" +dependencies = [ + "base64 0.21.0", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-rustls", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-rustls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", + "winreg", +] + +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint", + "hmac 0.12.1", + "zeroize", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + [[package]] name = "ripemd" version = "0.1.3" @@ -1938,6 +3903,31 @@ dependencies = [ "digest 0.10.6", ] +[[package]] +name = "rkyv" +version = "0.7.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cec2b3485b07d96ddfd3134767b8a447b45ea4eb91448d0a35180ec0ffd5ed15" +dependencies = [ + "bytecheck", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eaedadc88b53e36dd32d940ed21ae4d850d5916f2581526921f553a72ac34c4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "rlp" version = "0.5.2" @@ -1948,12 +3938,71 @@ dependencies = [ "rustc-hex", ] +[[package]] +name = "rlp-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "rust_decimal" +version = "1.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33c321ee4e17d2b7abe12b5d20c1231db708dd36185c8a21e9de5fed6da4dbe9" +dependencies = [ + "arrayvec", + "borsh", + "bytecheck", + "byteorder", + "bytes", + "num-traits", + "rand", + "rkyv", + "serde", + "serde_json", +] + [[package]] name = "rustc-hex" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustls" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +dependencies = [ + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +dependencies = [ + "base64 0.21.0", +] + [[package]] name = "rustversion" version = "1.0.11" @@ -1966,12 +4015,133 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" +[[package]] +name = "salsa20" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0fbb5f676da676c260ba276a8f43a8dc67cf02d1438423aeb1c677a7212686" +dependencies = [ + "cipher 0.3.0", +] + +[[package]] +name = "salsa20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" +dependencies = [ + "cipher 0.4.3", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scale-info" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "001cf62ece89779fd16105b5f515ad0e5cedcd5440d3dd806bb067978e7c3608" +dependencies = [ + "cfg-if", + "derive_more", + "parity-scale-codec", + "scale-info-derive", +] + +[[package]] +name = "scale-info-derive" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "303959cf613a6f6efd19ed4b4ad5bf79966a13352716299ad532cfb115f4205c" +dependencies = [ + "proc-macro-crate 1.1.3", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "scrypt" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e73d6d7c6311ebdbd9184ad6c4447b2f36337e327bda107d3ba9e3c374f9d325" +dependencies = [ + "hmac 0.12.1", + "password-hash 0.3.2", + "pbkdf2 0.10.1", + "salsa20 0.9.0", + "sha2 0.10.6", +] + +[[package]] +name = "scrypt" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f9e24d2b632954ded8ab2ef9fea0a0c769ea56ea98bddbafbad22caeeadf45d" +dependencies = [ + "hmac 0.12.1", + "pbkdf2 0.11.0", + "salsa20 0.10.2", + "sha2 0.10.6", +] + +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct", + "der", + "generic-array 0.14.6", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" +dependencies = [ + "serde", +] + +[[package]] +name = "send_wrapper" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "930c0acf610d3fdb5e2ab6213019aaa04e227ebe9547b0649ba599b16d788bd7" + [[package]] name = "serde" version = "1.0.152" @@ -1981,6 +4151,26 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-aux" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a77223b653fa95f3f9864f3eb25b93e4ed170687eb42d85b6b98af21d5e1de" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "serde-aux" +version = "4.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c599b3fd89a75e0c18d6d2be693ddb12cccaf771db4ff9e39097104808a014c0" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "serde-big-array" version = "0.3.3" @@ -2055,14 +4245,38 @@ dependencies = [ ] [[package]] -name = "serde_tuple_macros" -version = "0.5.0" +name = "serde_tuple_macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4076151d1a2b688e25aaf236997933c66e18b870d0369f8b248b8ab2be630d7e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4076151d1a2b688e25aaf236997933c66e18b870d0369f8b248b8ab2be630d7e" +checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" dependencies = [ - "proc-macro2", - "quote", - "syn", + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", ] [[package]] @@ -2075,7 +4289,7 @@ dependencies = [ "cfg-if", "cpufeatures", "digest 0.9.0", - "opaque-debug", + "opaque-debug 0.3.0", ] [[package]] @@ -2099,6 +4313,16 @@ dependencies = [ "keccak", ] +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest 0.10.6", + "rand_core", +] + [[package]] name = "slab" version = "0.4.7" @@ -2108,6 +4332,34 @@ dependencies = [ "autocfg", ] +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "snafu" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0656e7e3ffb70f6c39b3c2a86332bb74aa3c679da781642590f3c1118c5045" +dependencies = [ + "doc-comment", + "snafu-derive", +] + +[[package]] +name = "snafu-derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "475b3bbe5245c26f2d8a6f62d67c1f30eb9fffeccee721c45d162c3ebbdf81b2" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "socket2" version = "0.4.7" @@ -2118,12 +4370,69 @@ dependencies = [ "winapi", ] +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spki" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strsim" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + +[[package]] +name = "substrate-bn" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b5bbfa79abbae15dd642ea8176a21a635ff3c00059961d1ea27ad04e5b441c" +dependencies = [ + "byteorder", + "crunchy", + "lazy_static", + "rand", + "rustc-hex", +] + [[package]] name = "subtle" version = "2.4.1" @@ -2153,6 +4462,12 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "termcolor" version = "1.2.0" @@ -2192,9 +4507,11 @@ dependencies = [ "bimap", "blake2b_simd", "cid", + "ethers 0.17.0", "fil_actor_account", "fil_actor_cron", "fil_actor_datacap", + "fil_actor_eam", "fil_actor_ethaccount", "fil_actor_evm", "fil_actor_init", @@ -2206,6 +4523,7 @@ dependencies = [ "fil_actor_reward", "fil_actor_system", "fil_actor_verifreg", + "fil_actors_evm_shared", "fil_actors_runtime", "fil_builtin_actors_state", "frc46_token", @@ -2215,9 +4533,12 @@ dependencies = [ "fvm_ipld_encoding 0.3.3", "fvm_ipld_hamt", "fvm_shared 3.0.0-alpha.20", + "hex", + "hex-literal", "indexmap", "integer-encoding", "lazy_static", + "libsecp256k1", "log", "multihash", "num-derive", @@ -2256,6 +4577,72 @@ dependencies = [ "syn", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a12a59981d9e3c38d216785b0c37399f6e415e8d0712047620f189371b0bb" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "pin-project-lite", + "socket2", + "windows-sys", +] + +[[package]] +name = "tokio-rustls" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +dependencies = [ + "rustls", + "tokio", + "webpki", +] + +[[package]] +name = "tokio-util" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + [[package]] name = "toml" version = "0.5.11" @@ -2265,18 +4652,111 @@ dependencies = [ "serde", ] +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + [[package]] name = "typenum" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +[[package]] +name = "ucd-trie" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" + [[package]] name = "unicode-ident" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" + [[package]] name = "unicode-xid" version = "0.2.4" @@ -2289,6 +4769,33 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d86a8dc7f45e4c1b0d30e43038c38f274e77af056aa5f74b93c2cf9eb3c1c836" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "uuid" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +dependencies = [ + "getrandom", + "serde", +] + [[package]] name = "value-bag" version = "1.0.0-alpha.9" @@ -2311,6 +4818,27 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -2383,6 +4911,21 @@ version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +[[package]] +name = "wasm-timer" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" +dependencies = [ + "futures", + "js-sys", + "parking_lot", + "pin-utils", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "web-sys" version = "0.3.60" @@ -2393,6 +4936,25 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" +dependencies = [ + "webpki", +] + [[package]] name = "wepoll-ffi" version = "0.1.2" @@ -2489,3 +5051,45 @@ name = "windows_x86_64_msvc" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + +[[package]] +name = "ws_stream_wasm" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47ca1ab42f5afed7fc332b22b6e932ca5414b209465412c8cdf0ad23bc0de645" +dependencies = [ + "async_io_stream", + "futures", + "js-sys", + "pharos", + "rustc_version", + "send_wrapper", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "zeroize" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" diff --git a/Cargo.toml b/Cargo.toml index faa049e16..339b2c84b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ fil_actor_cron = { version = "10.0.0-alpha.1", path = "./actors/cron", features fil_actor_datacap = { version = "10.0.0-alpha.1", path = "./actors/datacap", features = ["fil-actor"] } fil_actor_ethaccount = { version = "10.0.0-alpha.1", path = "actors/ethaccount", features = ["fil-actor"] } fil_actor_evm = { version = "10.0.0-alpha.1", path = "./actors/evm", features = ["fil-actor"] } +fil_actor_eam = { version = "10.0.0-alpha.1", path = "./actors/eam", features = ["fil-actor"] } fil_actor_init = { version = "10.0.0-alpha.1", path = "./actors/init", features = ["fil-actor"] } fil_actor_market = { version = "10.0.0-alpha.1", path = "./actors/market", features = ["fil-actor"] } fil_actor_miner = { version = "10.0.0-alpha.1", path = "./actors/miner", features = ["fil-actor"] } @@ -39,7 +40,7 @@ num-traits = "0.2.15" clap = { version = "3.2.3", features = ["derive"] } [features] -default = [] ## translates to mainnet +default = [] ## translates to mainnet mainnet = [] caterpillarnet = [] butterflynet = [] @@ -47,14 +48,10 @@ calibrationnet = [] devnet = [] testing = [] testing-fake-proofs = [] +m2-native = [] [workspace] -members = [ - "actors/*", - "state", - "runtime", - "test_vm", -] +members = ["actors/*", "state", "runtime", "test_vm", "actors/evm/shared"] [patch.crates-io] #fvm_shared = { git = "https://github.com/filecoin-project/ref-fvm", branch = "master" } @@ -71,22 +68,24 @@ members = [ ## Uncomment when working locally on ref-fvm and this repo simultaneously. ## Assumes the ref-fvm checkout is in a sibling directory with the same name. ## (Valid while FVM modules aren't published to crates.io) -#[patch."https://github.com/filecoin-project/ref-fvm"] -#fvm_shared = { path = "../ref-fvm/shared" } -#fvm_sdk = { path = "../ref-fvm/sdk" } -#fvm_ipld_hamt = { path = "../ref-fvm/ipld/hamt" } -#fvm_ipld_amt = { path = "../ref-fvm/ipld/amt" } -#fvm_ipld_bitfield = { path = "../ref-fvm/ipld/bitfield"} -#fvm_ipld_encoding = { path = "../ref-fvm/ipld/encoding"} -#fvm_ipld_blockstore = { path = "../ref-fvm/ipld/blockstore"} - -## Uncomment entries below when working locally on ref-fvm and this repo simultaneously. -## Assumes the ref-fvm checkout is in a sibling directory with the same name. -## (Valid once FVM modules are published to crates.io) +##[patch."https://github.com/filecoin-project/ref-fvm"] +fvm_shared = { path = "../ref-fvm/shared" } +fvm_sdk = { path = "../ref-fvm/sdk" } +fvm_ipld_hamt = { path = "../ref-fvm/ipld/hamt" } +fvm_ipld_kamt = { path = "../ref-fvm/ipld/kamt" } +fvm_ipld_amt = { path = "../ref-fvm/ipld/amt" } +fvm_ipld_bitfield = { path = "../ref-fvm/ipld/bitfield"} +fvm_ipld_encoding = { path = "../ref-fvm/ipld/encoding"} +fvm_ipld_blockstore = { path = "../ref-fvm/ipld/blockstore"} + +# Uncomment entries below when working locally on ref-fvm and this repo simultaneously. +# Assumes the ref-fvm checkout is in a sibling directory with the same name. +# (Valid once FVM modules are published to crates.io) # [patch.crates-io] # fvm_shared = { path = "../ref-fvm/shared" } # fvm_sdk = { path = "../ref-fvm/sdk" } # fvm_ipld_hamt = { path = "../ref-fvm/ipld/hamt" } +# fvm_ipld_kamt = { path = "../ref-fvm/ipld/kamt" } # fvm_ipld_amt = { path = "../ref-fvm/ipld/amt" } # fvm_ipld_bitfield = { path = "../ref-fvm/ipld/bitfield"} # fvm_ipld_encoding = { path = "../ref-fvm/ipld/encoding"} diff --git a/Makefile b/Makefile index db5a24dee..cea35aeff 100644 --- a/Makefile +++ b/Makefile @@ -45,7 +45,7 @@ bundle: deps-build cargo run -- -o output/builtin-actors.car # Create all canonical network bundles -all-bundles: bundle-mainnet bundle-caterpillarnet bundle-butterflynet bundle-calibrationnet bundle-devnet bundle-testing bundle-testing +all-bundles: bundle-mainnet bundle-caterpillarnet bundle-butterflynet bundle-calibrationnet bundle-devnet bundle-testing bundle-testing bundle-wallaby bundle-hyperspace bundle-mainnet: deps-build BUILD_FIL_NETWORK=mainnet cargo run -- -o output/builtin-actors-mainnet.car @@ -62,6 +62,15 @@ bundle-calibrationnet: deps-build bundle-devnet: deps-build BUILD_FIL_NETWORK=devnet cargo run -- -o output/builtin-actors-devnet.car +bundle-wallaby: deps-build + BUILD_FIL_NETWORK=wallaby cargo run -- -o output/builtin-actors-wallaby.car + +bundle-hyperspace: deps-build + BUILD_FIL_NETWORK=hyperspace cargo run -- -o output/builtin-actors-hyperspace.car + +bundle-devnet-wasm: deps-build + BUILD_FIL_NETWORK=devnet-wasm cargo run -- -o output/builtin-actors-devnet-wasm.car + bundle-testing: deps-build BUILD_FIL_NETWORK=testing cargo run -- -o output/builtin-actors-testing.car BUILD_FIL_NETWORK=testing-fake-proofs cargo run -- -o output/builtin-actors-testing-fake-proofs.car diff --git a/README.md b/README.md index 98f0e70b4..b3eb1cde3 100644 --- a/README.md +++ b/README.md @@ -219,3 +219,6 @@ of the implementation or project they identify with. Dual-licensed: [MIT](./LICENSE-MIT), [Apache Software License v2](./LICENSE-APACHE), by way of the [Permissive License Stack](https://protocol.ai/blog/announcing-the-permissive-license-stack/). + +Except the EVM precompile [test data](actors/evm/precompile-testdata), which is licensed under the +LGPL v3 and not included in crates or build artifacts. diff --git a/actors/account/Cargo.toml b/actors/account/Cargo.toml index ee879693b..b2690de9c 100644 --- a/actors/account/Cargo.toml +++ b/actors/account/Cargo.toml @@ -16,12 +16,12 @@ crate-type = ["cdylib", "lib"] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } frc42_dispatch = "3.0.1-alpha.2" fvm_actor_utils = "4.0.1-alpha.1" -fvm_shared = { version = "3.0.0-alpha.20", default-features = false } +fvm_shared = { path = "../../../ref-fvm/shared", default-features = false } serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" num-derive = "0.3.3" -fvm_ipld_blockstore = "0.1.1" -fvm_ipld_encoding = "0.3.3" +fvm_ipld_blockstore = { path = "../../../ref-fvm/ipld/blockstore" } +fvm_ipld_encoding = { path = "../../../ref-fvm/ipld/encoding"} anyhow = "1.0.65" [dev-dependencies] diff --git a/actors/account/tests/account_actor_test.rs b/actors/account/tests/account_actor_test.rs index a4817146a..fc432e2e8 100644 --- a/actors/account/tests/account_actor_test.rs +++ b/actors/account/tests/account_actor_test.rs @@ -72,7 +72,7 @@ fn token_receiver() { ) .unwrap(); - rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1000)); + rt.set_caller(*EVM_ACTOR_CODE_ID, Address::new_id(1000)); rt.expect_validate_caller_any(); let ret = rt .call::( diff --git a/actors/cron/Cargo.toml b/actors/cron/Cargo.toml index 97847fe72..a2c52c169 100644 --- a/actors/cron/Cargo.toml +++ b/actors/cron/Cargo.toml @@ -15,13 +15,13 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.20", default-features = false } +fvm_shared = { path = "../../../ref-fvm/shared", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" serde = { version = "1.0.136", features = ["derive"] } -fvm_ipld_blockstore = "0.1.1" -fvm_ipld_encoding = "0.3.3" +fvm_ipld_blockstore = { path = "../../../ref-fvm/ipld/blockstore" } +fvm_ipld_encoding = { path = "../../../ref-fvm/ipld/encoding"} [dev-dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["test_utils", "sector-default"] } diff --git a/actors/datacap/Cargo.toml b/actors/datacap/Cargo.toml index c411876be..a0e47ed01 100644 --- a/actors/datacap/Cargo.toml +++ b/actors/datacap/Cargo.toml @@ -20,10 +20,10 @@ cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] frc42_dispatch = "3.0.1-alpha.2" frc46_token = "4.0.1-alpha.1" fvm_actor_utils = "4.0.1-alpha.1" -fvm_ipld_blockstore = "0.1.1" -fvm_ipld_encoding = "0.3.3" -fvm_ipld_hamt = "0.6.1" -fvm_shared = { version = "3.0.0-alpha.20", default-features = false } +fvm_ipld_blockstore = { path = "../../../ref-fvm/ipld/blockstore" } +fvm_ipld_encoding = { path = "../../../ref-fvm/ipld/encoding"} +fvm_ipld_hamt = { path = "../../../ref-fvm/ipld/hamt" } +fvm_shared = { path = "../../../ref-fvm/shared", default-features = false } lazy_static = "1.4.0" num-derive = "0.3.3" num-traits = "0.2.14" @@ -34,4 +34,3 @@ log = "0.4.14" fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] } [features] fil-actor = ["fil_actors_runtime/fil-actor"] - diff --git a/actors/datacap/src/lib.rs b/actors/datacap/src/lib.rs index 2c4976546..9d81e791e 100644 --- a/actors/datacap/src/lib.rs +++ b/actors/datacap/src/lib.rs @@ -13,8 +13,7 @@ use fvm_shared::address::Address; use fvm_shared::bigint::BigInt; use fvm_shared::econ::TokenAmount; use fvm_shared::error::{ErrorNumber, ExitCode}; -use fvm_shared::Response; -use fvm_shared::{ActorID, MethodNum, METHOD_CONSTRUCTOR}; +use fvm_shared::{ActorID, MethodNum, Response, METHOD_CONSTRUCTOR}; use lazy_static::lazy_static; use log::info; use num_derive::FromPrimitive; diff --git a/actors/eam/Cargo.toml b/actors/eam/Cargo.toml index 016197509..0b62acccc 100644 --- a/actors/eam/Cargo.toml +++ b/actors/eam/Cargo.toml @@ -20,11 +20,11 @@ serde_tuple = "0.5" rlp = { version = "0.5.1", default-features = false } anyhow = "1.0.65" log = "0.4.14" -fvm_ipld_blockstore = "0.1.1" -fvm_ipld_encoding = "0.3.3" +fvm_ipld_blockstore = { path = "../../../ref-fvm/ipld/blockstore" } +fvm_ipld_encoding = { path = "../../../ref-fvm/ipld/encoding"} multihash = { version = "0.16.1", default-features = false } cid = "0.8.6" -fvm_shared = { version = "3.0.0-alpha.20", default-features = false } +fvm_shared = { path = "../../../ref-fvm/shared", default-features = false } num-traits = "0.2.15" num-derive = "0.3.3" hex-literal = "0.3.4" diff --git a/actors/eam/src/ext.rs b/actors/eam/src/ext.rs index f359d97f2..30deea364 100644 --- a/actors/eam/src/ext.rs +++ b/actors/eam/src/ext.rs @@ -4,7 +4,6 @@ use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; pub mod init { - use super::*; pub const EXEC4_METHOD: u64 = 3; @@ -29,6 +28,7 @@ pub mod init { pub mod evm { use super::*; + use fil_actors_evm_shared::address::EthAddress; #[derive(Serialize_tuple, Deserialize_tuple, Clone)] diff --git a/actors/eam/src/lib.rs b/actors/eam/src/lib.rs index bd8b2611e..3785068c0 100644 --- a/actors/eam/src/lib.rs +++ b/actors/eam/src/lib.rs @@ -107,7 +107,7 @@ fn hash_20(rt: &impl Runtime, data: &[u8]) -> [u8; 20] { } fn can_assign_address(addr: &EthAddress) -> bool { - !addr.is_id() && !addr.is_null() + !addr.is_precompile() && !addr.is_id() && !addr.is_null() } fn create_actor( @@ -322,6 +322,21 @@ mod test { create_actor(&mut rt, creator, new_addr, Vec::new()).unwrap_err().exit_code() ); + // Reject EVM Precompile. + let mut new_addr = EthAddress::null(); + new_addr.0[19] = 0x20; + assert_eq!( + ExitCode::USR_FORBIDDEN, + create_actor(&mut rt, creator, new_addr, Vec::new()).unwrap_err().exit_code() + ); + + // Reject Native Precompile. + new_addr.0[0] = 0xfe; + assert_eq!( + ExitCode::USR_FORBIDDEN, + create_actor(&mut rt, creator, new_addr, Vec::new()).unwrap_err().exit_code() + ); + // Reject Null. let new_addr = EthAddress::null(); assert_eq!( diff --git a/actors/ethaccount/Cargo.toml b/actors/ethaccount/Cargo.toml index 01cc89aa7..52003b413 100644 --- a/actors/ethaccount/Cargo.toml +++ b/actors/ethaccount/Cargo.toml @@ -18,8 +18,8 @@ fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } frc42_dispatch = "3.0.1-alpha.2" fvm_actor_utils = "4.0.1-alpha.1" serde = { version = "1.0.136", features = ["derive"] } -fvm_ipld_encoding = "0.3.3" -fvm_shared = { version = "3.0.0-alpha.20", default-features = false } +fvm_ipld_encoding = { path = "../../../ref-fvm/ipld/encoding"} +fvm_shared = { path = "../../../ref-fvm/shared", default-features = false } num-traits = "0.2.15" num-derive = "0.3.3" hex-literal = "0.3.4" diff --git a/actors/evm/Cargo.toml b/actors/evm/Cargo.toml index b2379dbd7..c12423ecd 100644 --- a/actors/evm/Cargo.toml +++ b/actors/evm/Cargo.toml @@ -9,11 +9,41 @@ repository = "https://github.com/filecoin-project/builtin-actors" keywords = ["filecoin", "web3", "wasm", "evm"] exclude = ["/precompile-testdata", "/tests/measurements", "/tests/contracts"] +[lib] +## lib is necessary for integration tests +## cdylib is necessary for Wasm build +crate-type = ["cdylib", "lib"] + [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } +fvm_shared = { path = "../../../ref-fvm/shared", default-features = false } +fvm_ipld_kamt = { path = "../../../ref-fvm/ipld/kamt" } serde = { version = "1.0.136", features = ["derive"] } -fvm_ipld_encoding = "0.3.3" -fil_actors_evm_shared = { version = "10.0.0-alpha.1", path = "shared" } +serde_tuple = "0.5" +num-traits = "0.2.14" +num-derive = "0.3.3" +cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] } +anyhow = "1.0.65" +log = "0.4.14" +fvm_ipld_blockstore = { path = "../../../ref-fvm/ipld/blockstore"} +fvm_ipld_encoding = { path = "../../../ref-fvm/ipld/encoding"} +multihash = { version = "0.16.1", default-features = false } +hex = { version = "0.4.3", features = ["serde"] } +hex-literal = "0.3.4" +substrate-bn = { version = "0.6.0", default-features = false } +frc42_dispatch = "3.0.1-alpha.2" +fil_actors_evm_shared = { path = "shared" } + +[dev-dependencies] +lazy_static = "1.4.0" +fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] } +etk-asm = "^0.2.1" +ethers = { version = "1.0.2", features = ["abigen"] } +serde_json = "1.0" +rand = "0.8.5" +once_cell = "1.17.0" [features] +hyperspace = [] fil-actor = ["fil_actors_runtime/fil-actor"] +m2-native = ["fil_actors_runtime/m2-native"] diff --git a/actors/evm/Makefile b/actors/evm/Makefile new file mode 100644 index 000000000..abca4fd7d --- /dev/null +++ b/actors/evm/Makefile @@ -0,0 +1,68 @@ +TEST_CONTRACTS_DIR = tests/contracts +TEST_CONTRACTS_SOL = $(shell find $(TEST_CONTRACTS_DIR) -type f -name "*.sol") +TEST_CONTRACTS_HEX = $(TEST_CONTRACTS_SOL:.sol=.hex) + +MEASUREMENTS_DIR = tests/measurements +MEASUREMENTS_JSON = $(shell find $(MEASUREMENTS_DIR) -type f -name "*.jsonline") +MEASUREMENTS_PNG = $(MEASUREMENTS_JSON:.jsonline=.png) + +.PHONY: all +all: \ + test-contracts \ + measure-storage-footprint \ + plot-measurements + +# Compile all Solidity test contracts. +# This could also be achieved with https://docs.rs/ethers/latest/ethers/solc/ +.PHONY: test-contracts +test-contracts: $(TEST_CONTRACTS_HEX) + +# Compile a Solidity test contract +$(TEST_CONTRACTS_DIR)/%.hex: $(TEST_CONTRACTS_DIR)/%.sol | solc + @# Generate the .hex file which is the same as the .bin, but .bin would be renamed by solc to use CamelCase. + @# We could use just the .bin files in the test, but this is a way to stick to the existing pattern. + solc --bin $< | sed '4q;d' | tr -d '\n' > $@ + solc --bin --abi --storage-layout --hashes --overwrite $< -o $(TEST_CONTRACTS_DIR) + + +# Run storage footprint tests. +.PHONY: measure-storage-footprint +measure-storage-footprint: + cargo test --test storage_footprint + + +# Render measurement charts. +.PHONY: plot-measurements +plot-measurements: $(MEASUREMENTS_PNG) + +# Render a specfic plot if the data changed. +$(MEASUREMENTS_DIR)/%.png: \ + $(MEASUREMENTS_DIR)/%.jsonline \ + $(MEASUREMENTS_DIR)/storage-footprint.plt \ + $(MEASUREMENTS_DIR)/storage-footprint.sh \ + | jq gnuplot + cd $(MEASUREMENTS_DIR) && ./storage-footprint.sh $* + + +# Requirements checks. + +.PHONY: solc +solc: + @if [ -z "$(shell which solc)" ]; then \ + echo "Please install solc, the Solidity compiler. See https://github.com/crytic/solc-select"; \ + exit 1; \ + fi + +.PHONY: gnuplot +gnuplot: + @if [ -z "$(shell which gnuplot)" ]; then \ + echo "Please install gnuplot. See http://www.gnuplot.info/"; \ + exit 1; \ + fi + +.PHONY: jq +jq: + @if [ -z "$(shell which jq)" ]; then \ + echo "Please install jq. See https://stedolan.github.io/jq/"; \ + exit 1; \ + fi diff --git a/actors/evm/README.md b/actors/evm/README.md new file mode 100644 index 000000000..aff3876e0 --- /dev/null +++ b/actors/evm/README.md @@ -0,0 +1,11 @@ +# EVM + +The EVM actor is a Wasm implementation of the EVM bytecode interpreter, originally prototyped in https://github.com/filecoin-project/fvm-evm/ + +## Testing + +The `tests` library contains integration tests, some of which use Solidity contracts. The compiled versions of these contracts are checked into [tests/contracts](./tests/contracts). To modify them, you will need to install the [solc](https://docs.soliditylang.org/en/latest/installing-solidity.html) and optionally [solc-select](https://github.com/crytic/solc-select), then from this directory run the following command to generate the new artifacts: + +```shell +make test-contracts +``` diff --git a/actors/evm/precompile-testdata/LICENSE b/actors/evm/precompile-testdata/LICENSE new file mode 100644 index 000000000..02bbb60bc --- /dev/null +++ b/actors/evm/precompile-testdata/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. \ No newline at end of file diff --git a/actors/evm/precompile-testdata/blake2F.json b/actors/evm/precompile-testdata/blake2F.json new file mode 100644 index 000000000..a25f9ae50 --- /dev/null +++ b/actors/evm/precompile-testdata/blake2F.json @@ -0,0 +1,37 @@ +[ + { + "Input": "0000000048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001", + "Expected": "08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d282e6ad7f520e511f6c3e2b8c68059b9442be0454267ce079217e1319cde05b", + "Name": "vector 4", + "Gas": 0, + "NoBenchmark": false + }, + { + "Input": "0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001", + "Expected": "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923", + "Name": "vector 5", + "Gas": 12, + "NoBenchmark": false + }, + { + "Input": "0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000", + "Expected": "75ab69d3190a562c51aef8d88f1c2775876944407270c42c9844252c26d2875298743e7f6d5ea2f2d3e8d226039cd31b4e426ac4f2d3d666a610c2116fde4735", + "Name": "vector 6", + "Gas": 12, + "NoBenchmark": false + }, + { + "Input": "0000000148c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001", + "Expected": "b63a380cb2897d521994a85234ee2c181b5f844d2c624c002677e9703449d2fba551b3a8333bcdf5f2f7e08993d53923de3d64fcc68c034e717b9293fed7a421", + "Name": "vector 7", + "Gas": 1, + "NoBenchmark": false + }, + { + "Input": "007A120048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001", + "Expected": "6d2ce9e534d50e18ff866ae92d70cceba79bbcd14c63819fe48752c8aca87a4bb7dcc230d22a4047f0486cfcfb50a17b24b2899eb8fca370f22240adb5170189", + "Name": "vector 8", + "Gas": 8000000, + "NoBenchmark": false + } +] \ No newline at end of file diff --git a/actors/evm/precompile-testdata/bn256Add.json b/actors/evm/precompile-testdata/bn256Add.json new file mode 100644 index 000000000..b6fcd550e --- /dev/null +++ b/actors/evm/precompile-testdata/bn256Add.json @@ -0,0 +1,114 @@ +[ + { + "Input": "18b18acfb4c2c30276db5411368e7185b311dd124691610c5d3b74034e093dc9063c909c4720840cb5134cb9f59fa749755796819658d32efc0d288198f3726607c2b7f58a84bd6145f00c9c2bc0bb1a187f20ff2c92963a88019e7c6a014eed06614e20c147e940f2d70da3f74c9a17df361706a4485c742bd6788478fa17d7", + "Expected": "2243525c5efd4b9c3d3c45ac0ca3fe4dd85e830a4ce6b65fa1eeaee202839703301d1d33be6da8e509df21cc35964723180eed7532537db9ae5e7d48f195c915", + "Name": "chfast1", + "Gas": 150, + "NoBenchmark": false + }, + { + "Input": "2243525c5efd4b9c3d3c45ac0ca3fe4dd85e830a4ce6b65fa1eeaee202839703301d1d33be6da8e509df21cc35964723180eed7532537db9ae5e7d48f195c91518b18acfb4c2c30276db5411368e7185b311dd124691610c5d3b74034e093dc9063c909c4720840cb5134cb9f59fa749755796819658d32efc0d288198f37266", + "Expected": "2bd3e6d0f3b142924f5ca7b49ce5b9d54c4703d7ae5648e61d02268b1a0a9fb721611ce0a6af85915e2f1d70300909ce2e49dfad4a4619c8390cae66cefdb204", + "Name": "chfast2", + "Gas": 150, + "NoBenchmark": false + }, + { + "Input": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "Expected": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "Name": "cdetrio1", + "Gas": 150, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "Expected": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "Name": "cdetrio2", + "Gas": 150, + "NoBenchmark": false + }, + { + "Input": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "Expected": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "Name": "cdetrio3", + "Gas": 150, + "NoBenchmark": false + }, + { + "Input": "", + "Expected": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "Name": "cdetrio4", + "Gas": 150, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "Expected": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "Name": "cdetrio5", + "Gas": 150, + "NoBenchmark": false + }, + { + "Input": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", + "Expected": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", + "Name": "cdetrio6", + "Gas": 150, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "Expected": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", + "Name": "cdetrio7", + "Gas": 150, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", + "Expected": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", + "Name": "cdetrio8", + "Gas": 150, + "NoBenchmark": false + }, + { + "Input": "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "Expected": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", + "Gas": 150, + "Name": "cdetrio9", + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "Expected": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", + "Gas": 150, + "Name": "cdetrio10", + "NoBenchmark": false + }, + { + "Input": "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", + "Expected": "030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd315ed738c0e0a7c92e7845f96b2ae9c0a68a6a449e3538fc7ff3ebf7a5a18a2c4", + "Name": "cdetrio11", + "Gas": 150, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "Expected": "030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd315ed738c0e0a7c92e7845f96b2ae9c0a68a6a449e3538fc7ff3ebf7a5a18a2c4", + "Name": "cdetrio12", + "Gas": 150, + "NoBenchmark": false + }, + { + "Input": "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa901e0559bacb160664764a357af8a9fe70baa9258e0b959273ffc5718c6d4cc7c039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869073a5ffcc6fc7a28c30723d6e58ce577356982d65b833a5a5c15bf9024b43d98", + "Expected": "15bf2bb17880144b5d1cd2b1f46eff9d617bffd1ca57c37fb5a49bd84e53cf66049c797f9ce0d17083deb32b5e36f2ea2a212ee036598dd7624c168993d1355f", + "Name": "cdetrio13", + "Gas": 150, + "NoBenchmark": false + }, + { + "Input": "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa901e0559bacb160664764a357af8a9fe70baa9258e0b959273ffc5718c6d4cc7c17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa92e83f8d734803fc370eba25ed1f6b8768bd6d83887b87165fc2434fe11a830cb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "Expected": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "Name": "cdetrio14", + "Gas": 150, + "NoBenchmark": false + } +] \ No newline at end of file diff --git a/actors/evm/precompile-testdata/bn256Pairing.json b/actors/evm/precompile-testdata/bn256Pairing.json new file mode 100644 index 000000000..3fbed6b87 --- /dev/null +++ b/actors/evm/precompile-testdata/bn256Pairing.json @@ -0,0 +1,100 @@ +[ + { + "Input": "1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f593034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf704bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a416782bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "Expected": "0000000000000000000000000000000000000000000000000000000000000001", + "Name": "jeff1", + "Gas": 113000, + "NoBenchmark": false + }, + { + "Input": "2eca0c7238bf16e83e7a1e6c5d49540685ff51380f309842a98561558019fc0203d3260361bb8451de5ff5ecd17f010ff22f5c31cdf184e9020b06fa5997db841213d2149b006137fcfb23036606f848d638d576a120ca981b5b1a5f9300b3ee2276cf730cf493cd95d64677bbb75fc42db72513a4c1e387b476d056f80aa75f21ee6226d31426322afcda621464d0611d226783262e21bb3bc86b537e986237096df1f82dff337dd5972e32a8ad43e28a78a96a823ef1cd4debe12b6552ea5f06967a1237ebfeca9aaae0d6d0bab8e28c198c5a339ef8a2407e31cdac516db922160fa257a5fd5b280642ff47b65eca77e626cb685c84fa6d3b6882a283ddd1198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "Expected": "0000000000000000000000000000000000000000000000000000000000000001", + "Name": "jeff2", + "Gas": 113000, + "NoBenchmark": false + }, + { + "Input": "0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd216da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba2e89718ad33c8bed92e210e81d1853435399a271913a6520736a4729cf0d51eb01a9e2ffa2e92599b68e44de5bcf354fa2642bd4f26b259daa6f7ce3ed57aeb314a9a87b789a58af499b314e13c3d65bede56c07ea2d418d6874857b70763713178fb49a2d6cd347dc58973ff49613a20757d0fcc22079f9abd10c3baee245901b9e027bd5cfc2cb5db82d4dc9677ac795ec500ecd47deee3b5da006d6d049b811d7511c78158de484232fc68daf8a45cf217d1c2fae693ff5871e8752d73b21198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "Expected": "0000000000000000000000000000000000000000000000000000000000000001", + "Name": "jeff3", + "Gas": 113000, + "NoBenchmark": false + }, + { + "Input": "2f2ea0b3da1e8ef11914acf8b2e1b32d99df51f5f4f206fc6b947eae860eddb6068134ddb33dc888ef446b648d72338684d678d2eb2371c61a50734d78da4b7225f83c8b6ab9de74e7da488ef02645c5a16a6652c3c71a15dc37fe3a5dcb7cb122acdedd6308e3bb230d226d16a105295f523a8a02bfc5e8bd2da135ac4c245d065bbad92e7c4e31bf3757f1fe7362a63fbfee50e7dc68da116e67d600d9bf6806d302580dc0661002994e7cd3a7f224e7ddc27802777486bf80f40e4ca3cfdb186bac5188a98c45e6016873d107f5cd131f3a3e339d0375e58bd6219347b008122ae2b09e539e152ec5364e7e2204b03d11d3caa038bfc7cd499f8176aacbee1f39e4e4afc4bc74790a4a028aff2c3d2538731fb755edefd8cb48d6ea589b5e283f150794b6736f670d6a1033f9b46c6f5204f50813eb85c8dc4b59db1c5d39140d97ee4d2b36d99bc49974d18ecca3e7ad51011956051b464d9e27d46cc25e0764bb98575bd466d32db7b15f582b2d5c452b36aa394b789366e5e3ca5aabd415794ab061441e51d01e94640b7e3084a07e02c78cf3103c542bc5b298669f211b88da1679b0b64a63b7e0e7bfe52aae524f73a55be7fe70c7e9bfc94b4cf0da1213d2149b006137fcfb23036606f848d638d576a120ca981b5b1a5f9300b3ee2276cf730cf493cd95d64677bbb75fc42db72513a4c1e387b476d056f80aa75f21ee6226d31426322afcda621464d0611d226783262e21bb3bc86b537e986237096df1f82dff337dd5972e32a8ad43e28a78a96a823ef1cd4debe12b6552ea5f", + "Expected": "0000000000000000000000000000000000000000000000000000000000000001", + "Name": "jeff4", + "Gas": 147000, + "NoBenchmark": false + }, + { + "Input": "20a754d2071d4d53903e3b31a7e98ad6882d58aec240ef981fdf0a9d22c5926a29c853fcea789887315916bbeb89ca37edb355b4f980c9a12a94f30deeed30211213d2149b006137fcfb23036606f848d638d576a120ca981b5b1a5f9300b3ee2276cf730cf493cd95d64677bbb75fc42db72513a4c1e387b476d056f80aa75f21ee6226d31426322afcda621464d0611d226783262e21bb3bc86b537e986237096df1f82dff337dd5972e32a8ad43e28a78a96a823ef1cd4debe12b6552ea5f1abb4a25eb9379ae96c84fff9f0540abcfc0a0d11aeda02d4f37e4baf74cb0c11073b3ff2cdbb38755f8691ea59e9606696b3ff278acfc098fa8226470d03869217cee0a9ad79a4493b5253e2e4e3a39fc2df38419f230d341f60cb064a0ac290a3d76f140db8418ba512272381446eb73958670f00cf46f1d9e64cba057b53c26f64a8ec70387a13e41430ed3ee4a7db2059cc5fc13c067194bcc0cb49a98552fd72bd9edb657346127da132e5b82ab908f5816c826acb499e22f2412d1a2d70f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd2198a1f162a73261f112401aa2db79c7dab1533c9935c77290a6ce3b191f2318d198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "Expected": "0000000000000000000000000000000000000000000000000000000000000001", + "Name": "jeff5", + "Gas": 147000, + "NoBenchmark": false + }, + { + "Input": "1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f593034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf704bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a416782bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c103188585e2364128fe25c70558f1560f4f9350baf3959e603cc91486e110936198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "Expected": "0000000000000000000000000000000000000000000000000000000000000000", + "Name": "jeff6", + "Gas": 113000, + "NoBenchmark": false + }, + { + "Input": "", + "Expected": "0000000000000000000000000000000000000000000000000000000000000001", + "Name": "empty_data", + "Gas": 45000, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "Expected": "0000000000000000000000000000000000000000000000000000000000000000", + "Name": "one_point", + "Gas": 79000, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d", + "Expected": "0000000000000000000000000000000000000000000000000000000000000001", + "Name": "two_point_match_2", + "Gas": 113000, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "Expected": "0000000000000000000000000000000000000000000000000000000000000001", + "Name": "two_point_match_3", + "Gas": 113000, + "NoBenchmark": false + }, + { + "Input": "105456a333e6d636854f987ea7bb713dfd0ae8371a72aea313ae0c32c0bf10160cf031d41b41557f3e7e3ba0c51bebe5da8e6ecd855ec50fc87efcdeac168bcc0476be093a6d2b4bbf907172049874af11e1b6267606e00804d3ff0037ec57fd3010c68cb50161b7d1d96bb71edfec9880171954e56871abf3d93cc94d745fa114c059d74e5b6c4ec14ae5864ebe23a71781d86c29fb8fb6cce94f70d3de7a2101b33461f39d9e887dbb100f170a2345dde3c07e256d1dfa2b657ba5cd030427000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000021a2c3013d2ea92e13c800cde68ef56a294b883f6ac35d25f587c09b1b3c635f7290158a80cd3d66530f74dc94c94adb88f5cdb481acca997b6e60071f08a115f2f997f3dbd66a7afe07fe7862ce239edba9e05c5afff7f8a1259c9733b2dfbb929d1691530ca701b4a106054688728c9972c8512e9789e9567aae23e302ccd75", + "Expected": "0000000000000000000000000000000000000000000000000000000000000001", + "Name": "two_point_match_4", + "Gas": 113000, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d", + "Expected": "0000000000000000000000000000000000000000000000000000000000000001", + "Name": "ten_point_match_1", + "Gas": 385000, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "Expected": "0000000000000000000000000000000000000000000000000000000000000001", + "Name": "ten_point_match_2", + "Gas": 385000, + "NoBenchmark": false + }, + { + "Input": "105456a333e6d636854f987ea7bb713dfd0ae8371a72aea313ae0c32c0bf10160cf031d41b41557f3e7e3ba0c51bebe5da8e6ecd855ec50fc87efcdeac168bcc0476be093a6d2b4bbf907172049874af11e1b6267606e00804d3ff0037ec57fd3010c68cb50161b7d1d96bb71edfec9880171954e56871abf3d93cc94d745fa114c059d74e5b6c4ec14ae5864ebe23a71781d86c29fb8fb6cce94f70d3de7a2101b33461f39d9e887dbb100f170a2345dde3c07e256d1dfa2b657ba5cd030427000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000021a2c3013d2ea92e13c800cde68ef56a294b883f6ac35d25f587c09b1b3c635f7290158a80cd3d66530f74dc94c94adb88f5cdb481acca997b6e60071f08a115f2f997f3dbd66a7afe07fe7862ce239edba9e05c5afff7f8a1259c9733b2dfbb929d1691530ca701b4a106054688728c9972c8512e9789e9567aae23e302ccd75", + "Expected": "0000000000000000000000000000000000000000000000000000000000000001", + "Name": "ten_point_match_3", + "Gas": 113000, + "NoBenchmark": false + } +] \ No newline at end of file diff --git a/actors/evm/precompile-testdata/bn256ScalarMul.json b/actors/evm/precompile-testdata/bn256ScalarMul.json new file mode 100644 index 000000000..2a28f6304 --- /dev/null +++ b/actors/evm/precompile-testdata/bn256ScalarMul.json @@ -0,0 +1,128 @@ +[ + { + "Input": "2bd3e6d0f3b142924f5ca7b49ce5b9d54c4703d7ae5648e61d02268b1a0a9fb721611ce0a6af85915e2f1d70300909ce2e49dfad4a4619c8390cae66cefdb20400000000000000000000000000000000000000000000000011138ce750fa15c2", + "Expected": "070a8d6a982153cae4be29d434e8faef8a47b274a053f5a4ee2a6c9c13c31e5c031b8ce914eba3a9ffb989f9cdd5b0f01943074bf4f0f315690ec3cec6981afc", + "Name": "chfast1", + "Gas": 6000, + "NoBenchmark": false + }, + { + "Input": "070a8d6a982153cae4be29d434e8faef8a47b274a053f5a4ee2a6c9c13c31e5c031b8ce914eba3a9ffb989f9cdd5b0f01943074bf4f0f315690ec3cec6981afc30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd46", + "Expected": "025a6f4181d2b4ea8b724290ffb40156eb0adb514c688556eb79cdea0752c2bb2eff3f31dea215f1eb86023a133a996eb6300b44da664d64251d05381bb8a02e", + "Name": "chfast2", + "Gas": 6000, + "NoBenchmark": false + }, + { + "Input": "025a6f4181d2b4ea8b724290ffb40156eb0adb514c688556eb79cdea0752c2bb2eff3f31dea215f1eb86023a133a996eb6300b44da664d64251d05381bb8a02e183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7ea3", + "Expected": "14789d0d4a730b354403b5fac948113739e276c23e0258d8596ee72f9cd9d3230af18a63153e0ec25ff9f2951dd3fa90ed0197bfef6e2a1a62b5095b9d2b4a27", + "Name": "chfast3", + "Gas": 6000, + "NoBenchmark": false + }, + { + "Input": "1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f6ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "Expected": "2cde5879ba6f13c0b5aa4ef627f159a3347df9722efce88a9afbb20b763b4c411aa7e43076f6aee272755a7f9b84832e71559ba0d2e0b17d5f9f01755e5b0d11", + "Name": "cdetrio1", + "Gas": 6000, + "NoBenchmark": false + }, + { + "Input": "1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f630644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000", + "Expected": "1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe3163511ddc1c3f25d396745388200081287b3fd1472d8339d5fecb2eae0830451", + "Name": "cdetrio2", + "Gas": 6000, + "NoBenchmark": true + }, + { + "Input": "1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f60000000000000000000000000000000100000000000000000000000000000000", + "Expected": "1051acb0700ec6d42a88215852d582efbaef31529b6fcbc3277b5c1b300f5cf0135b2394bb45ab04b8bd7611bd2dfe1de6a4e6e2ccea1ea1955f577cd66af85b", + "Name": "cdetrio3", + "Gas": 6000, + "NoBenchmark": true + }, + { + "Input": "1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f60000000000000000000000000000000000000000000000000000000000000009", + "Expected": "1dbad7d39dbc56379f78fac1bca147dc8e66de1b9d183c7b167351bfe0aeab742cd757d51289cd8dbd0acf9e673ad67d0f0a89f912af47ed1be53664f5692575", + "Name": "cdetrio4", + "Gas": 6000, + "NoBenchmark": true + }, + { + "Input": "1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f60000000000000000000000000000000000000000000000000000000000000001", + "Expected": "1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f6", + "Name": "cdetrio5", + "Gas": 6000, + "NoBenchmark": true + }, + { + "Input": "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa901e0559bacb160664764a357af8a9fe70baa9258e0b959273ffc5718c6d4cc7cffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "Expected": "29e587aadd7c06722aabba753017c093f70ba7eb1f1c0104ec0564e7e3e21f6022b1143f6a41008e7755c71c3d00b6b915d386de21783ef590486d8afa8453b1", + "Name": "cdetrio6", + "Gas": 6000, + "NoBenchmark": false + }, + { + "Input": "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa901e0559bacb160664764a357af8a9fe70baa9258e0b959273ffc5718c6d4cc7c30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000", + "Expected": "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa92e83f8d734803fc370eba25ed1f6b8768bd6d83887b87165fc2434fe11a830cb", + "Name": "cdetrio7", + "Gas": 6000, + "NoBenchmark": true + }, + { + "Input": "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa901e0559bacb160664764a357af8a9fe70baa9258e0b959273ffc5718c6d4cc7c0000000000000000000000000000000100000000000000000000000000000000", + "Expected": "221a3577763877920d0d14a91cd59b9479f83b87a653bb41f82a3f6f120cea7c2752c7f64cdd7f0e494bff7b60419f242210f2026ed2ec70f89f78a4c56a1f15", + "Name": "cdetrio8", + "Gas": 6000, + "NoBenchmark": true + }, + { + "Input": "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa901e0559bacb160664764a357af8a9fe70baa9258e0b959273ffc5718c6d4cc7c0000000000000000000000000000000000000000000000000000000000000009", + "Expected": "228e687a379ba154554040f8821f4e41ee2be287c201aa9c3bc02c9dd12f1e691e0fd6ee672d04cfd924ed8fdc7ba5f2d06c53c1edc30f65f2af5a5b97f0a76a", + "Name": "cdetrio9", + "Gas": 6000, + "NoBenchmark": true + }, + { + "Input": "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa901e0559bacb160664764a357af8a9fe70baa9258e0b959273ffc5718c6d4cc7c0000000000000000000000000000000000000000000000000000000000000001", + "Expected": "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa901e0559bacb160664764a357af8a9fe70baa9258e0b959273ffc5718c6d4cc7c", + "Name": "cdetrio10", + "Gas": 6000, + "NoBenchmark": true + }, + { + "Input": "039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869073a5ffcc6fc7a28c30723d6e58ce577356982d65b833a5a5c15bf9024b43d98ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "Expected": "00a1a234d08efaa2616607e31eca1980128b00b415c845ff25bba3afcb81dc00242077290ed33906aeb8e42fd98c41bcb9057ba03421af3f2d08cfc441186024", + "Name": "cdetrio11", + "Gas": 6000, + "NoBenchmark": false + }, + { + "Input": "039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869073a5ffcc6fc7a28c30723d6e58ce577356982d65b833a5a5c15bf9024b43d9830644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000", + "Expected": "039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b8692929ee761a352600f54921df9bf472e66217e7bb0cee9032e00acc86b3c8bfaf", + "Name": "cdetrio12", + "Gas": 6000, + "NoBenchmark": true + }, + { + "Input": "039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869073a5ffcc6fc7a28c30723d6e58ce577356982d65b833a5a5c15bf9024b43d980000000000000000000000000000000100000000000000000000000000000000", + "Expected": "1071b63011e8c222c5a771dfa03c2e11aac9666dd097f2c620852c3951a4376a2f46fe2f73e1cf310a168d56baa5575a8319389d7bfa6b29ee2d908305791434", + "Name": "cdetrio13", + "Gas": 6000, + "NoBenchmark": true + }, + { + "Input": "039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869073a5ffcc6fc7a28c30723d6e58ce577356982d65b833a5a5c15bf9024b43d980000000000000000000000000000000000000000000000000000000000000009", + "Expected": "19f75b9dd68c080a688774a6213f131e3052bd353a304a189d7a2ee367e3c2582612f545fb9fc89fde80fd81c68fc7dcb27fea5fc124eeda69433cf5c46d2d7f", + "Name": "cdetrio14", + "Gas": 6000, + "NoBenchmark": true + }, + { + "Input": "039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869073a5ffcc6fc7a28c30723d6e58ce577356982d65b833a5a5c15bf9024b43d980000000000000000000000000000000000000000000000000000000000000001", + "Expected": "039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869073a5ffcc6fc7a28c30723d6e58ce577356982d65b833a5a5c15bf9024b43d98", + "Name": "cdetrio15", + "Gas": 6000, + "NoBenchmark": true + } +] \ No newline at end of file diff --git a/actors/evm/precompile-testdata/ecRecover.json b/actors/evm/precompile-testdata/ecRecover.json new file mode 100644 index 000000000..4911d6157 --- /dev/null +++ b/actors/evm/precompile-testdata/ecRecover.json @@ -0,0 +1,37 @@ +[ + { + "Input": "a8b53bdf3306a35a7103ab5504a0c9b492295564b6202b1942a84ef300107281000000000000000000000000000000000000000000000000000000000000001b307835653165303366353363653138623737326363623030393366663731663366353366356337356237346463623331613835616138623838393262346538621122334455667788991011121314151617181920212223242526272829303132", + "Expected": "", + "Gas": 3000, + "Name": "CallEcrecoverUnrecoverableKey", + "NoBenchmark": false + }, + { + "Input": "18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c000000000000000000000000000000000000000000000000000000000000001c73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75feeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549", + "Expected": "000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "Gas": 3000, + "Name": "ValidKey", + "NoBenchmark": false + }, + { + "Input": "18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c100000000000000000000000000000000000000000000000000000000000001c73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75feeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549", + "Expected": "", + "Gas": 3000, + "Name": "InvalidHighV-bits-1", + "NoBenchmark": false + }, + { + "Input": "18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c000000000000000000000000000000000000001000000000000000000000001c73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75feeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549", + "Expected": "", + "Gas": 3000, + "Name": "InvalidHighV-bits-2", + "NoBenchmark": false + }, + { + "Input": "18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c000000000000000000000000000000000000001000000000000000000000011c73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75feeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549", + "Expected": "", + "Gas": 3000, + "Name": "InvalidHighV-bits-3", + "NoBenchmark": false + } +] \ No newline at end of file diff --git a/actors/evm/precompile-testdata/fail-blake2f.json b/actors/evm/precompile-testdata/fail-blake2f.json new file mode 100644 index 000000000..70835aa5b --- /dev/null +++ b/actors/evm/precompile-testdata/fail-blake2f.json @@ -0,0 +1,22 @@ +[ + { + "Input": "", + "ExpectedError": "invalid input length", + "Name": "vector 0: empty input" + }, + { + "Input": "00000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001", + "ExpectedError": "invalid input length", + "Name": "vector 1: less than 213 bytes input" + }, + { + "Input": "000000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001", + "ExpectedError": "invalid input length", + "Name": "vector 2: more than 213 bytes input" + }, + { + "Input": "0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000002", + "ExpectedError": "invalid final flag", + "Name": "vector 3: malformed final block indicator flag" + } +] \ No newline at end of file diff --git a/actors/evm/precompile-testdata/modexp.json b/actors/evm/precompile-testdata/modexp.json new file mode 100644 index 000000000..4550eb913 --- /dev/null +++ b/actors/evm/precompile-testdata/modexp.json @@ -0,0 +1,121 @@ +[ + { + "Input": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002003fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2efffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "Expected": "0000000000000000000000000000000000000000000000000000000000000001", + "Name": "eip_example1", + "Gas": 13056, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2efffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "Expected": "0000000000000000000000000000000000000000000000000000000000000000", + "Name": "eip_example2", + "Gas": 13056, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb502fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b", + "Expected": "60008f1614cc01dcfb6bfb09c625cf90b47d4468db81b5f8b7a39d42f332eab9b2da8f2d95311648a8f243f4bb13cfb3d8f7f2a3c014122ebb3ed41b02783adc", + "Name": "nagydani-1-square", + "Gas": 204, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb503fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b", + "Expected": "4834a46ba565db27903b1c720c9d593e84e4cbd6ad2e64b31885d944f68cd801f92225a8961c952ddf2797fa4701b330c85c4b363798100b921a1a22a46a7fec", + "Name": "nagydani-1-qube", + "Gas": 204, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb5010001fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b", + "Expected": "c36d804180c35d4426b57b50c5bfcca5c01856d104564cd513b461d3c8b8409128a5573e416d0ebe38f5f736766d9dc27143e4da981dfa4d67f7dc474cbee6d2", + "Name": "nagydani-1-pow0x10001", + "Gas": 3276, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf5102e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087", + "Expected": "981dd99c3b113fae3e3eaa9435c0dc96779a23c12a53d1084b4f67b0b053a27560f627b873e3f16ad78f28c94f14b6392def26e4d8896c5e3c984e50fa0b3aa44f1da78b913187c6128baa9340b1e9c9a0fd02cb78885e72576da4a8f7e5a113e173a7a2889fde9d407bd9f06eb05bc8fc7b4229377a32941a02bf4edcc06d70", + "Name": "nagydani-2-square", + "Gas": 665, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf5103e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087", + "Expected": "d89ceb68c32da4f6364978d62aaa40d7b09b59ec61eb3c0159c87ec3a91037f7dc6967594e530a69d049b64adfa39c8fa208ea970cfe4b7bcd359d345744405afe1cbf761647e32b3184c7fbe87cee8c6c7ff3b378faba6c68b83b6889cb40f1603ee68c56b4c03d48c595c826c041112dc941878f8c5be828154afd4a16311f", + "Name": "nagydani-2-qube", + "Gas": 665, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf51010001e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087", + "Expected": "ad85e8ef13fd1dd46eae44af8b91ad1ccae5b7a1c92944f92a19f21b0b658139e0cabe9c1f679507c2de354bf2c91ebd965d1e633978a830d517d2f6f8dd5fd58065d58559de7e2334a878f8ec6992d9b9e77430d4764e863d77c0f87beede8f2f7f2ab2e7222f85cc9d98b8467f4bb72e87ef2882423ebdb6daf02dddac6db2", + "Name": "nagydani-2-pow0x10001", + "Gas": 10649, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb02d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d", + "Expected": "affc7507ea6d84751ec6b3f0d7b99dbcc263f33330e450d1b3ff0bc3d0874320bf4edd57debd587306988157958cb3cfd369cc0c9c198706f635c9e0f15d047df5cb44d03e2727f26b083c4ad8485080e1293f171c1ed52aef5993a5815c35108e848c951cf1e334490b4a539a139e57b68f44fee583306f5b85ffa57206b3ee5660458858534e5386b9584af3c7f67806e84c189d695e5eb96e1272d06ec2df5dc5fabc6e94b793718c60c36be0a4d031fc84cd658aa72294b2e16fc240aef70cb9e591248e38bd49c5a554d1afa01f38dab72733092f7555334bbef6c8c430119840492380aa95fa025dcf699f0a39669d812b0c6946b6091e6e235337b6f8", + "Name": "nagydani-3-square", + "Gas": 1894, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb03d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d", + "Expected": "1b280ecd6a6bf906b806d527c2a831e23b238f89da48449003a88ac3ac7150d6a5e9e6b3be4054c7da11dd1e470ec29a606f5115801b5bf53bc1900271d7c3ff3cd5ed790d1c219a9800437a689f2388ba1a11d68f6a8e5b74e9a3b1fac6ee85fc6afbac599f93c391f5dc82a759e3c6c0ab45ce3f5d25d9b0c1bf94cf701ea6466fc9a478dacc5754e593172b5111eeba88557048bceae401337cd4c1182ad9f700852bc8c99933a193f0b94cf1aedbefc48be3bc93ef5cb276d7c2d5462ac8bb0c8fe8923a1db2afe1c6b90d59c534994a6a633f0ead1d638fdc293486bb634ff2c8ec9e7297c04241a61c37e3ae95b11d53343d4ba2b4cc33d2cfa7eb705e", + "Name": "nagydani-3-qube", + "Gas": 1894, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb010001d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d", + "Expected": "37843d7c67920b5f177372fa56e2a09117df585f81df8b300fba245b1175f488c99476019857198ed459ed8d9799c377330e49f4180c4bf8e8f66240c64f65ede93d601f957b95b83efdee1e1bfde74169ff77002eaf078c71815a9220c80b2e3b3ff22c2f358111d816ebf83c2999026b6de50bfc711ff68705d2f40b753424aefc9f70f08d908b5a20276ad613b4ab4309a3ea72f0c17ea9df6b3367d44fb3acab11c333909e02e81ea2ed404a712d3ea96bba87461720e2d98723e7acd0520ac1a5212dbedcd8dc0c1abf61d4719e319ff4758a774790b8d463cdfe131d1b2dcfee52d002694e98e720cb6ae7ccea353bc503269ba35f0f63bf8d7b672a76", + "Name": "nagydani-3-pow0x10001", + "Gas": 30310, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b8102df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f", + "Expected": "8a5aea5f50dcc03dc7a7a272b5aeebc040554dbc1ffe36753c4fc75f7ed5f6c2cc0de3a922bf96c78bf0643a73025ad21f45a4a5cadd717612c511ab2bff1190fe5f1ae05ba9f8fe3624de1de2a817da6072ddcdb933b50216811dbe6a9ca79d3a3c6b3a476b079fd0d05f04fb154e2dd3e5cb83b148a006f2bcbf0042efb2ae7b916ea81b27aac25c3bf9a8b6d35440062ad8eae34a83f3ffa2cc7b40346b62174a4422584f72f95316f6b2bee9ff232ba9739301c97c99a9ded26c45d72676eb856ad6ecc81d36a6de36d7f9dafafee11baa43a4b0d5e4ecffa7b9b7dcefd58c397dd373e6db4acd2b2c02717712e6289bed7c813b670c4a0c6735aa7f3b0f1ce556eae9fcc94b501b2c8781ba50a8c6220e8246371c3c7359fe4ef9da786ca7d98256754ca4e496be0a9174bedbecb384bdf470779186d6a833f068d2838a88d90ef3ad48ff963b67c39cc5a3ee123baf7bf3125f64e77af7f30e105d72c4b9b5b237ed251e4c122c6d8c1405e736299c3afd6db16a28c6a9cfa68241e53de4cd388271fe534a6a9b0dbea6171d170db1b89858468885d08fecbd54c8e471c3e25d48e97ba450b96d0d87e00ac732aaa0d3ce4309c1064bd8a4c0808a97e0143e43a24cfa847635125cd41c13e0574487963e9d725c01375db99c31da67b4cf65eff555f0c0ac416c727ff8d438ad7c42030551d68c2e7adda0abb1ca7c10", + "Name": "nagydani-4-square", + "Gas": 5580, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b8103df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f", + "Expected": "5a2664252aba2d6e19d9600da582cdd1f09d7a890ac48e6b8da15ae7c6ff1856fc67a841ac2314d283ffa3ca81a0ecf7c27d89ef91a5a893297928f5da0245c99645676b481b7e20a566ee6a4f2481942bee191deec5544600bb2441fd0fb19e2ee7d801ad8911c6b7750affec367a4b29a22942c0f5f4744a4e77a8b654da2a82571037099e9c6d930794efe5cdca73c7b6c0844e386bdca8ea01b3d7807146bb81365e2cdc6475f8c23e0ff84463126189dc9789f72bbce2e3d2d114d728a272f1345122de23df54c922ec7a16e5c2a8f84da8871482bd258c20a7c09bbcd64c7a96a51029bbfe848736a6ba7bf9d931a9b7de0bcaf3635034d4958b20ae9ab3a95a147b0421dd5f7ebff46c971010ebfc4adbbe0ad94d5498c853e7142c450d8c71de4b2f84edbf8acd2e16d00c8115b150b1c30e553dbb82635e781379fe2a56360420ff7e9f70cc64c00aba7e26ed13c7c19622865ae07248daced36416080f35f8cc157a857ed70ea4f347f17d1bee80fa038abd6e39b1ba06b97264388b21364f7c56e192d4b62d9b161405f32ab1e2594e86243e56fcf2cb30d21adef15b9940f91af681da24328c883d892670c6aa47940867a81830a82b82716895db810df1b834640abefb7db2092dd92912cb9a735175bc447be40a503cf22dfe565b4ed7a3293ca0dfd63a507430b323ee248ec82e843b673c97ad730728cebc", + "Name": "nagydani-4-qube", + "Gas": 5580, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b81010001df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f", + "Expected": "bed8b970c4a34849fc6926b08e40e20b21c15ed68d18f228904878d4370b56322d0da5789da0318768a374758e6375bfe4641fca5285ec7171828922160f48f5ca7efbfee4d5148612c38ad683ae4e3c3a053d2b7c098cf2b34f2cb19146eadd53c86b2d7ccf3d83b2c370bfb840913ee3879b1057a6b4e07e110b6bcd5e958bc71a14798c91d518cc70abee264b0d25a4110962a764b364ac0b0dd1ee8abc8426d775ec0f22b7e47b32576afaf1b5a48f64573ed1c5c29f50ab412188d9685307323d990802b81dacc06c6e05a1e901830ba9fcc67688dc29c5e27bde0a6e845ca925f5454b6fb3747edfaa2a5820838fb759eadf57f7cb5cec57fc213ddd8a4298fa079c3c0f472b07fb15aa6a7f0a3780bd296ff6a62e58ef443870b02260bd4fd2bbc98255674b8e1f1f9f8d33c7170b0ebbea4523b695911abbf26e41885344823bd0587115fdd83b721a4e8457a31c9a84b3d3520a07e0e35df7f48e5a9d534d0ec7feef1ff74de6a11e7f93eab95175b6ce22c68d78a642ad642837897ec11349205d8593ac19300207572c38d29ca5dfa03bc14cdbc32153c80e5cc3e739403d34c75915e49beb43094cc6dcafb3665b305ddec9286934ae66ec6b777ca528728c851318eb0f207b39f1caaf96db6eeead6b55ed08f451939314577d42bcc9f97c0b52d0234f88fd07e4c1d7780fdebc025cfffcb572cb27a8c33963", + "Name": "nagydani-4-pow0x10001", + "Gas": 89292, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf02e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad", + "Expected": "d61fe4e3f32ac260915b5b03b78a86d11bfc41d973fce5b0cc59035cf8289a8a2e3878ea15fa46565b0d806e2f85b53873ea20ed653869b688adf83f3ef444535bf91598ff7e80f334fb782539b92f39f55310cc4b35349ab7b278346eda9bc37c0d8acd3557fae38197f412f8d9e57ce6a76b7205c23564cab06e5615be7c6f05c3d05ec690cba91da5e89d55b152ff8dd2157dc5458190025cf94b1ad98f7cbe64e9482faba95e6b33844afc640892872b44a9932096508f4a782a4805323808f23e54b6ff9b841dbfa87db3505ae4f687972c18ea0f0d0af89d36c1c2a5b14560c153c3fee406f5cf15cfd1c0bb45d767426d465f2f14c158495069d0c5955a00150707862ecaae30624ebacdd8ac33e4e6aab3ff90b6ba445a84689386b9e945d01823a65874444316e83767290fcff630d2477f49d5d8ffdd200e08ee1274270f86ed14c687895f6caf5ce528bd970c20d2408a9ba66216324c6a011ac4999098362dbd98a038129a2d40c8da6ab88318aa3046cb660327cc44236d9e5d2163bd0959062195c51ed93d0088b6f92051fc99050ece2538749165976233697ab4b610385366e5ce0b02ad6b61c168ecfbedcdf74278a38de340fd7a5fead8e588e294795f9b011e2e60377a89e25c90e145397cdeabc60fd32444a6b7642a611a83c464d8b8976666351b4865c37b02e6dc21dbcdf5f930341707b618cc0f03c3122646b3385c9df9f2ec730eec9d49e7dfc9153b6e6289da8c4f0ebea9ccc1b751948e3bb7171c9e4d57423b0eeeb79095c030cb52677b3f7e0b45c30f645391f3f9c957afa549c4e0b2465b03c67993cd200b1af01035962edbc4c9e89b31c82ac121987d6529dafdeef67a132dc04b6dc68e77f22862040b75e2ceb9ff16da0fca534e6db7bd12fa7b7f51b6c08c1e23dfcdb7acbd2da0b51c87ffbced065a612e9b1c8bba9b7e2d8d7a2f04fcc4aaf355b60d764879a76b5e16762d5f2f55d585d0c8e82df6940960cddfb72c91dfa71f6b4e1c6ca25dfc39a878e998a663c04fe29d5e83b9586d047b4d7ff70a9f0d44f127e7d741685ca75f11629128d916a0ffef4be586a30c4b70389cc746e84ebf177c01ee8a4511cfbb9d1ecf7f7b33c7dd8177896e10bbc82f838dcd6db7ac67de62bf46b6a640fb580c5d1d2708f3862e3d2b645d0d18e49ef088053e3a220adc0e033c2afcfe61c90e32151152eb3caaf746c5e377d541cafc6cbb0cc0fa48b5caf1728f2e1957f5addfc234f1a9d89e40d49356c9172d0561a695fce6dab1d412321bbf407f63766ffd7b6b3d79bcfa07991c5a9709849c1008689e3b47c50d613980bec239fb64185249d055b30375ccb4354d71fe4d05648fbf6c80634dfc3575f2f24abb714c1e4c95e8896763bf4316e954c7ad19e5780ab7a040ca6fb9271f90a8b22ae738daf6cb", + "Name": "nagydani-5-square", + "Gas": 17868, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf03e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad", + "Expected": "5f9c70ec884926a89461056ad20ac4c30155e817f807e4d3f5bb743d789c83386762435c3627773fa77da5144451f2a8aad8adba88e0b669f5377c5e9bad70e45c86fe952b613f015a9953b8a5de5eaee4566acf98d41e327d93a35bd5cef4607d025e58951167957df4ff9b1627649d3943805472e5e293d3efb687cfd1e503faafeb2840a3e3b3f85d016051a58e1c9498aab72e63b748d834b31eb05d85dcde65e27834e266b85c75cc4ec0135135e0601cb93eeeb6e0010c8ceb65c4c319623c5e573a2c8c9fbbf7df68a930beb412d3f4dfd146175484f45d7afaa0d2e60684af9b34730f7c8438465ad3e1d0c3237336722f2aa51095bd5759f4b8ab4dda111b684aa3dac62a761722e7ae43495b7709933512c81c4e3c9133a51f7ce9f2b51fcec064f65779666960b4e45df3900f54311f5613e8012dd1b8efd359eda31a778264c72aa8bb419d862734d769076bce2810011989a45374e5c5d8729fec21427f0bf397eacbb4220f603cf463a4b0c94efd858ffd9768cd60d6ce68d755e0fbad007ce5c2223d70c7018345a102e4ab3c60a13a9e7794303156d4c2063e919f2153c13961fb324c80b240742f47773a7a8e25b3e3fb19b00ce839346c6eb3c732fbc6b888df0b1fe0a3d07b053a2e9402c267b2d62f794d8a2840526e3ade15ce2264496ccd7519571dfde47f7a4bb16292241c20b2be59f3f8fb4f6383f232d838c5a22d8c95b6834d9d2ca493f5a505ebe8899503b0e8f9b19e6e2dd81c1628b80016d02097e0134de51054c4e7674824d4d758760fc52377d2cad145e259aa2ffaf54139e1a66b1e0c1c191e32ac59474c6b526f5b3ba07d3e5ec286eddf531fcd5292869be58c9f22ef91026159f7cf9d05ef66b4299f4da48cc1635bf2243051d342d378a22c83390553e873713c0454ce5f3234397111ac3fe3207b86f0ed9fc025c81903e1748103692074f83824fda6341be4f95ff00b0a9a208c267e12fa01825054cc0513629bf3dbb56dc5b90d4316f87654a8be18227978ea0a8a522760cad620d0d14fd38920fb7321314062914275a5f99f677145a6979b156bd82ecd36f23f8e1273cc2759ecc0b2c69d94dad5211d1bed939dd87ed9e07b91d49713a6e16ade0a98aea789f04994e318e4ff2c8a188cd8d43aeb52c6daa3bc29b4af50ea82a247c5cd67b573b34cbadcc0a376d3bbd530d50367b42705d870f2e27a8197ef46070528bfe408360faa2ebb8bf76e9f388572842bcb119f4d84ee34ae31f5cc594f23705a49197b181fb78ed1ec99499c690f843a4d0cf2e226d118e9372271054fbabdcc5c92ae9fefaef0589cd0e722eaf30c1703ec4289c7fd81beaa8a455ccee5298e31e2080c10c366a6fcf56f7d13582ad0bcad037c612b710fc595b70fbefaaca23623b60c6c39b11beb8e5843b6b3dac60f", + "Name": "nagydani-5-qube", + "Gas": 17868, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf010001e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad", + "Expected": "5a0eb2bdf0ac1cae8e586689fa16cd4b07dfdedaec8a110ea1fdb059dd5253231b6132987598dfc6e11f86780428982d50cf68f67ae452622c3b336b537ef3298ca645e8f89ee39a26758206a5a3f6409afc709582f95274b57b71fae5c6b74619ae6f089a5393c5b79235d9caf699d23d88fb873f78379690ad8405e34c19f5257d596580c7a6a7206a3712825afe630c76b31cdb4a23e7f0632e10f14f4e282c81a66451a26f8df2a352b5b9f607a7198449d1b926e27036810368e691a74b91c61afa73d9d3b99453e7c8b50fd4f09c039a2f2feb5c419206694c31b92df1d9586140cb3417b38d0c503c7b508cc2ed12e813a1c795e9829eb39ee78eeaf360a169b491a1d4e419574e712402de9d48d54c1ae5e03739b7156615e8267e1fb0a897f067afd11fb33f6e24182d7aaaaa18fe5bc1982f20d6b871e5a398f0f6f718181d31ec225cfa9a0a70124ed9a70031bdf0c1c7829f708b6e17d50419ef361cf77d99c85f44607186c8d683106b8bd38a49b5d0fb503b397a83388c5678dcfcc737499d84512690701ed621a6f0172aecf037184ddf0f2453e4053024018e5ab2e30d6d5363b56e8b41509317c99042f517247474ab3abc848e00a07f69c254f46f2a05cf6ed84e5cc906a518fdcfdf2c61ce731f24c5264f1a25fc04934dc28aec112134dd523f70115074ca34e3807aa4cb925147f3a0ce152d323bd8c675ace446d0fd1ae30c4b57f0eb2c23884bc18f0964c0114796c5b6d080c3d89175665fbf63a6381a6a9da39ad070b645c8bb1779506da14439a9f5b5d481954764ea114fac688930bc68534d403cff4210673b6a6ff7ae416b7cd41404c3d3f282fcd193b86d0f54d0006c2a503b40d5c3930da980565b8f9630e9493a79d1c03e74e5f93ac8e4dc1a901ec5e3b3e57049124c7b72ea345aa359e782285d9e6a5c144a378111dd02c40855ff9c2be9b48425cb0b2fd62dc8678fd151121cf26a65e917d65d8e0dacfae108eb5508b601fb8ffa370be1f9a8b749a2d12eeab81f41079de87e2d777994fa4d28188c579ad327f9957fb7bdecec5c680844dd43cb57cf87aeb763c003e65011f73f8c63442df39a92b946a6bd968a1c1e4d5fa7d88476a68bd8e20e5b70a99259c7d3f85fb1b65cd2e93972e6264e74ebf289b8b6979b9b68a85cd5b360c1987f87235c3c845d62489e33acf85d53fa3561fe3a3aee18924588d9c6eba4edb7a4d106b31173e42929f6f0c48c80ce6a72d54eca7c0fe870068b7a7c89c63cdda593f5b32d3cb4ea8a32c39f00ab449155757172d66763ed9527019d6de6c9f2416aa6203f4d11c9ebee1e1d3845099e55504446448027212616167eb36035726daa7698b075286f5379cd3e93cb3e0cf4f9cb8d017facbb5550ed32d5ec5400ae57e47e2bf78d1eaeff9480cc765ceff39db500", + "Name": "nagydani-5-pow0x10001", + "Gas": 285900, + "NoBenchmark": false + } +] \ No newline at end of file diff --git a/actors/evm/precompile-testdata/modexp_eip2565.json b/actors/evm/precompile-testdata/modexp_eip2565.json new file mode 100644 index 000000000..c55441439 --- /dev/null +++ b/actors/evm/precompile-testdata/modexp_eip2565.json @@ -0,0 +1,121 @@ +[ + { + "Input": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002003fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2efffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "Expected": "0000000000000000000000000000000000000000000000000000000000000001", + "Name": "eip_example1", + "Gas": 1360, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2efffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "Expected": "0000000000000000000000000000000000000000000000000000000000000000", + "Name": "eip_example2", + "Gas": 1360, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb502fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b", + "Expected": "60008f1614cc01dcfb6bfb09c625cf90b47d4468db81b5f8b7a39d42f332eab9b2da8f2d95311648a8f243f4bb13cfb3d8f7f2a3c014122ebb3ed41b02783adc", + "Name": "nagydani-1-square", + "Gas": 200, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb503fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b", + "Expected": "4834a46ba565db27903b1c720c9d593e84e4cbd6ad2e64b31885d944f68cd801f92225a8961c952ddf2797fa4701b330c85c4b363798100b921a1a22a46a7fec", + "Name": "nagydani-1-qube", + "Gas": 200, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb5010001fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b", + "Expected": "c36d804180c35d4426b57b50c5bfcca5c01856d104564cd513b461d3c8b8409128a5573e416d0ebe38f5f736766d9dc27143e4da981dfa4d67f7dc474cbee6d2", + "Name": "nagydani-1-pow0x10001", + "Gas": 341, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf5102e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087", + "Expected": "981dd99c3b113fae3e3eaa9435c0dc96779a23c12a53d1084b4f67b0b053a27560f627b873e3f16ad78f28c94f14b6392def26e4d8896c5e3c984e50fa0b3aa44f1da78b913187c6128baa9340b1e9c9a0fd02cb78885e72576da4a8f7e5a113e173a7a2889fde9d407bd9f06eb05bc8fc7b4229377a32941a02bf4edcc06d70", + "Name": "nagydani-2-square", + "Gas": 200, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf5103e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087", + "Expected": "d89ceb68c32da4f6364978d62aaa40d7b09b59ec61eb3c0159c87ec3a91037f7dc6967594e530a69d049b64adfa39c8fa208ea970cfe4b7bcd359d345744405afe1cbf761647e32b3184c7fbe87cee8c6c7ff3b378faba6c68b83b6889cb40f1603ee68c56b4c03d48c595c826c041112dc941878f8c5be828154afd4a16311f", + "Name": "nagydani-2-qube", + "Gas": 200, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf51010001e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087", + "Expected": "ad85e8ef13fd1dd46eae44af8b91ad1ccae5b7a1c92944f92a19f21b0b658139e0cabe9c1f679507c2de354bf2c91ebd965d1e633978a830d517d2f6f8dd5fd58065d58559de7e2334a878f8ec6992d9b9e77430d4764e863d77c0f87beede8f2f7f2ab2e7222f85cc9d98b8467f4bb72e87ef2882423ebdb6daf02dddac6db2", + "Name": "nagydani-2-pow0x10001", + "Gas": 1365, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb02d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d", + "Expected": "affc7507ea6d84751ec6b3f0d7b99dbcc263f33330e450d1b3ff0bc3d0874320bf4edd57debd587306988157958cb3cfd369cc0c9c198706f635c9e0f15d047df5cb44d03e2727f26b083c4ad8485080e1293f171c1ed52aef5993a5815c35108e848c951cf1e334490b4a539a139e57b68f44fee583306f5b85ffa57206b3ee5660458858534e5386b9584af3c7f67806e84c189d695e5eb96e1272d06ec2df5dc5fabc6e94b793718c60c36be0a4d031fc84cd658aa72294b2e16fc240aef70cb9e591248e38bd49c5a554d1afa01f38dab72733092f7555334bbef6c8c430119840492380aa95fa025dcf699f0a39669d812b0c6946b6091e6e235337b6f8", + "Name": "nagydani-3-square", + "Gas": 341, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb03d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d", + "Expected": "1b280ecd6a6bf906b806d527c2a831e23b238f89da48449003a88ac3ac7150d6a5e9e6b3be4054c7da11dd1e470ec29a606f5115801b5bf53bc1900271d7c3ff3cd5ed790d1c219a9800437a689f2388ba1a11d68f6a8e5b74e9a3b1fac6ee85fc6afbac599f93c391f5dc82a759e3c6c0ab45ce3f5d25d9b0c1bf94cf701ea6466fc9a478dacc5754e593172b5111eeba88557048bceae401337cd4c1182ad9f700852bc8c99933a193f0b94cf1aedbefc48be3bc93ef5cb276d7c2d5462ac8bb0c8fe8923a1db2afe1c6b90d59c534994a6a633f0ead1d638fdc293486bb634ff2c8ec9e7297c04241a61c37e3ae95b11d53343d4ba2b4cc33d2cfa7eb705e", + "Name": "nagydani-3-qube", + "Gas": 341, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb010001d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d", + "Expected": "37843d7c67920b5f177372fa56e2a09117df585f81df8b300fba245b1175f488c99476019857198ed459ed8d9799c377330e49f4180c4bf8e8f66240c64f65ede93d601f957b95b83efdee1e1bfde74169ff77002eaf078c71815a9220c80b2e3b3ff22c2f358111d816ebf83c2999026b6de50bfc711ff68705d2f40b753424aefc9f70f08d908b5a20276ad613b4ab4309a3ea72f0c17ea9df6b3367d44fb3acab11c333909e02e81ea2ed404a712d3ea96bba87461720e2d98723e7acd0520ac1a5212dbedcd8dc0c1abf61d4719e319ff4758a774790b8d463cdfe131d1b2dcfee52d002694e98e720cb6ae7ccea353bc503269ba35f0f63bf8d7b672a76", + "Name": "nagydani-3-pow0x10001", + "Gas": 5461, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b8102df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f", + "Expected": "8a5aea5f50dcc03dc7a7a272b5aeebc040554dbc1ffe36753c4fc75f7ed5f6c2cc0de3a922bf96c78bf0643a73025ad21f45a4a5cadd717612c511ab2bff1190fe5f1ae05ba9f8fe3624de1de2a817da6072ddcdb933b50216811dbe6a9ca79d3a3c6b3a476b079fd0d05f04fb154e2dd3e5cb83b148a006f2bcbf0042efb2ae7b916ea81b27aac25c3bf9a8b6d35440062ad8eae34a83f3ffa2cc7b40346b62174a4422584f72f95316f6b2bee9ff232ba9739301c97c99a9ded26c45d72676eb856ad6ecc81d36a6de36d7f9dafafee11baa43a4b0d5e4ecffa7b9b7dcefd58c397dd373e6db4acd2b2c02717712e6289bed7c813b670c4a0c6735aa7f3b0f1ce556eae9fcc94b501b2c8781ba50a8c6220e8246371c3c7359fe4ef9da786ca7d98256754ca4e496be0a9174bedbecb384bdf470779186d6a833f068d2838a88d90ef3ad48ff963b67c39cc5a3ee123baf7bf3125f64e77af7f30e105d72c4b9b5b237ed251e4c122c6d8c1405e736299c3afd6db16a28c6a9cfa68241e53de4cd388271fe534a6a9b0dbea6171d170db1b89858468885d08fecbd54c8e471c3e25d48e97ba450b96d0d87e00ac732aaa0d3ce4309c1064bd8a4c0808a97e0143e43a24cfa847635125cd41c13e0574487963e9d725c01375db99c31da67b4cf65eff555f0c0ac416c727ff8d438ad7c42030551d68c2e7adda0abb1ca7c10", + "Name": "nagydani-4-square", + "Gas": 1365, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b8103df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f", + "Expected": "5a2664252aba2d6e19d9600da582cdd1f09d7a890ac48e6b8da15ae7c6ff1856fc67a841ac2314d283ffa3ca81a0ecf7c27d89ef91a5a893297928f5da0245c99645676b481b7e20a566ee6a4f2481942bee191deec5544600bb2441fd0fb19e2ee7d801ad8911c6b7750affec367a4b29a22942c0f5f4744a4e77a8b654da2a82571037099e9c6d930794efe5cdca73c7b6c0844e386bdca8ea01b3d7807146bb81365e2cdc6475f8c23e0ff84463126189dc9789f72bbce2e3d2d114d728a272f1345122de23df54c922ec7a16e5c2a8f84da8871482bd258c20a7c09bbcd64c7a96a51029bbfe848736a6ba7bf9d931a9b7de0bcaf3635034d4958b20ae9ab3a95a147b0421dd5f7ebff46c971010ebfc4adbbe0ad94d5498c853e7142c450d8c71de4b2f84edbf8acd2e16d00c8115b150b1c30e553dbb82635e781379fe2a56360420ff7e9f70cc64c00aba7e26ed13c7c19622865ae07248daced36416080f35f8cc157a857ed70ea4f347f17d1bee80fa038abd6e39b1ba06b97264388b21364f7c56e192d4b62d9b161405f32ab1e2594e86243e56fcf2cb30d21adef15b9940f91af681da24328c883d892670c6aa47940867a81830a82b82716895db810df1b834640abefb7db2092dd92912cb9a735175bc447be40a503cf22dfe565b4ed7a3293ca0dfd63a507430b323ee248ec82e843b673c97ad730728cebc", + "Name": "nagydani-4-qube", + "Gas": 1365, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b81010001df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f", + "Expected": "bed8b970c4a34849fc6926b08e40e20b21c15ed68d18f228904878d4370b56322d0da5789da0318768a374758e6375bfe4641fca5285ec7171828922160f48f5ca7efbfee4d5148612c38ad683ae4e3c3a053d2b7c098cf2b34f2cb19146eadd53c86b2d7ccf3d83b2c370bfb840913ee3879b1057a6b4e07e110b6bcd5e958bc71a14798c91d518cc70abee264b0d25a4110962a764b364ac0b0dd1ee8abc8426d775ec0f22b7e47b32576afaf1b5a48f64573ed1c5c29f50ab412188d9685307323d990802b81dacc06c6e05a1e901830ba9fcc67688dc29c5e27bde0a6e845ca925f5454b6fb3747edfaa2a5820838fb759eadf57f7cb5cec57fc213ddd8a4298fa079c3c0f472b07fb15aa6a7f0a3780bd296ff6a62e58ef443870b02260bd4fd2bbc98255674b8e1f1f9f8d33c7170b0ebbea4523b695911abbf26e41885344823bd0587115fdd83b721a4e8457a31c9a84b3d3520a07e0e35df7f48e5a9d534d0ec7feef1ff74de6a11e7f93eab95175b6ce22c68d78a642ad642837897ec11349205d8593ac19300207572c38d29ca5dfa03bc14cdbc32153c80e5cc3e739403d34c75915e49beb43094cc6dcafb3665b305ddec9286934ae66ec6b777ca528728c851318eb0f207b39f1caaf96db6eeead6b55ed08f451939314577d42bcc9f97c0b52d0234f88fd07e4c1d7780fdebc025cfffcb572cb27a8c33963", + "Name": "nagydani-4-pow0x10001", + "Gas": 21845, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf02e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad", + "Expected": "d61fe4e3f32ac260915b5b03b78a86d11bfc41d973fce5b0cc59035cf8289a8a2e3878ea15fa46565b0d806e2f85b53873ea20ed653869b688adf83f3ef444535bf91598ff7e80f334fb782539b92f39f55310cc4b35349ab7b278346eda9bc37c0d8acd3557fae38197f412f8d9e57ce6a76b7205c23564cab06e5615be7c6f05c3d05ec690cba91da5e89d55b152ff8dd2157dc5458190025cf94b1ad98f7cbe64e9482faba95e6b33844afc640892872b44a9932096508f4a782a4805323808f23e54b6ff9b841dbfa87db3505ae4f687972c18ea0f0d0af89d36c1c2a5b14560c153c3fee406f5cf15cfd1c0bb45d767426d465f2f14c158495069d0c5955a00150707862ecaae30624ebacdd8ac33e4e6aab3ff90b6ba445a84689386b9e945d01823a65874444316e83767290fcff630d2477f49d5d8ffdd200e08ee1274270f86ed14c687895f6caf5ce528bd970c20d2408a9ba66216324c6a011ac4999098362dbd98a038129a2d40c8da6ab88318aa3046cb660327cc44236d9e5d2163bd0959062195c51ed93d0088b6f92051fc99050ece2538749165976233697ab4b610385366e5ce0b02ad6b61c168ecfbedcdf74278a38de340fd7a5fead8e588e294795f9b011e2e60377a89e25c90e145397cdeabc60fd32444a6b7642a611a83c464d8b8976666351b4865c37b02e6dc21dbcdf5f930341707b618cc0f03c3122646b3385c9df9f2ec730eec9d49e7dfc9153b6e6289da8c4f0ebea9ccc1b751948e3bb7171c9e4d57423b0eeeb79095c030cb52677b3f7e0b45c30f645391f3f9c957afa549c4e0b2465b03c67993cd200b1af01035962edbc4c9e89b31c82ac121987d6529dafdeef67a132dc04b6dc68e77f22862040b75e2ceb9ff16da0fca534e6db7bd12fa7b7f51b6c08c1e23dfcdb7acbd2da0b51c87ffbced065a612e9b1c8bba9b7e2d8d7a2f04fcc4aaf355b60d764879a76b5e16762d5f2f55d585d0c8e82df6940960cddfb72c91dfa71f6b4e1c6ca25dfc39a878e998a663c04fe29d5e83b9586d047b4d7ff70a9f0d44f127e7d741685ca75f11629128d916a0ffef4be586a30c4b70389cc746e84ebf177c01ee8a4511cfbb9d1ecf7f7b33c7dd8177896e10bbc82f838dcd6db7ac67de62bf46b6a640fb580c5d1d2708f3862e3d2b645d0d18e49ef088053e3a220adc0e033c2afcfe61c90e32151152eb3caaf746c5e377d541cafc6cbb0cc0fa48b5caf1728f2e1957f5addfc234f1a9d89e40d49356c9172d0561a695fce6dab1d412321bbf407f63766ffd7b6b3d79bcfa07991c5a9709849c1008689e3b47c50d613980bec239fb64185249d055b30375ccb4354d71fe4d05648fbf6c80634dfc3575f2f24abb714c1e4c95e8896763bf4316e954c7ad19e5780ab7a040ca6fb9271f90a8b22ae738daf6cb", + "Name": "nagydani-5-square", + "Gas": 5461, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf03e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad", + "Expected": "5f9c70ec884926a89461056ad20ac4c30155e817f807e4d3f5bb743d789c83386762435c3627773fa77da5144451f2a8aad8adba88e0b669f5377c5e9bad70e45c86fe952b613f015a9953b8a5de5eaee4566acf98d41e327d93a35bd5cef4607d025e58951167957df4ff9b1627649d3943805472e5e293d3efb687cfd1e503faafeb2840a3e3b3f85d016051a58e1c9498aab72e63b748d834b31eb05d85dcde65e27834e266b85c75cc4ec0135135e0601cb93eeeb6e0010c8ceb65c4c319623c5e573a2c8c9fbbf7df68a930beb412d3f4dfd146175484f45d7afaa0d2e60684af9b34730f7c8438465ad3e1d0c3237336722f2aa51095bd5759f4b8ab4dda111b684aa3dac62a761722e7ae43495b7709933512c81c4e3c9133a51f7ce9f2b51fcec064f65779666960b4e45df3900f54311f5613e8012dd1b8efd359eda31a778264c72aa8bb419d862734d769076bce2810011989a45374e5c5d8729fec21427f0bf397eacbb4220f603cf463a4b0c94efd858ffd9768cd60d6ce68d755e0fbad007ce5c2223d70c7018345a102e4ab3c60a13a9e7794303156d4c2063e919f2153c13961fb324c80b240742f47773a7a8e25b3e3fb19b00ce839346c6eb3c732fbc6b888df0b1fe0a3d07b053a2e9402c267b2d62f794d8a2840526e3ade15ce2264496ccd7519571dfde47f7a4bb16292241c20b2be59f3f8fb4f6383f232d838c5a22d8c95b6834d9d2ca493f5a505ebe8899503b0e8f9b19e6e2dd81c1628b80016d02097e0134de51054c4e7674824d4d758760fc52377d2cad145e259aa2ffaf54139e1a66b1e0c1c191e32ac59474c6b526f5b3ba07d3e5ec286eddf531fcd5292869be58c9f22ef91026159f7cf9d05ef66b4299f4da48cc1635bf2243051d342d378a22c83390553e873713c0454ce5f3234397111ac3fe3207b86f0ed9fc025c81903e1748103692074f83824fda6341be4f95ff00b0a9a208c267e12fa01825054cc0513629bf3dbb56dc5b90d4316f87654a8be18227978ea0a8a522760cad620d0d14fd38920fb7321314062914275a5f99f677145a6979b156bd82ecd36f23f8e1273cc2759ecc0b2c69d94dad5211d1bed939dd87ed9e07b91d49713a6e16ade0a98aea789f04994e318e4ff2c8a188cd8d43aeb52c6daa3bc29b4af50ea82a247c5cd67b573b34cbadcc0a376d3bbd530d50367b42705d870f2e27a8197ef46070528bfe408360faa2ebb8bf76e9f388572842bcb119f4d84ee34ae31f5cc594f23705a49197b181fb78ed1ec99499c690f843a4d0cf2e226d118e9372271054fbabdcc5c92ae9fefaef0589cd0e722eaf30c1703ec4289c7fd81beaa8a455ccee5298e31e2080c10c366a6fcf56f7d13582ad0bcad037c612b710fc595b70fbefaaca23623b60c6c39b11beb8e5843b6b3dac60f", + "Name": "nagydani-5-qube", + "Gas": 5461, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf010001e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad", + "Expected": "5a0eb2bdf0ac1cae8e586689fa16cd4b07dfdedaec8a110ea1fdb059dd5253231b6132987598dfc6e11f86780428982d50cf68f67ae452622c3b336b537ef3298ca645e8f89ee39a26758206a5a3f6409afc709582f95274b57b71fae5c6b74619ae6f089a5393c5b79235d9caf699d23d88fb873f78379690ad8405e34c19f5257d596580c7a6a7206a3712825afe630c76b31cdb4a23e7f0632e10f14f4e282c81a66451a26f8df2a352b5b9f607a7198449d1b926e27036810368e691a74b91c61afa73d9d3b99453e7c8b50fd4f09c039a2f2feb5c419206694c31b92df1d9586140cb3417b38d0c503c7b508cc2ed12e813a1c795e9829eb39ee78eeaf360a169b491a1d4e419574e712402de9d48d54c1ae5e03739b7156615e8267e1fb0a897f067afd11fb33f6e24182d7aaaaa18fe5bc1982f20d6b871e5a398f0f6f718181d31ec225cfa9a0a70124ed9a70031bdf0c1c7829f708b6e17d50419ef361cf77d99c85f44607186c8d683106b8bd38a49b5d0fb503b397a83388c5678dcfcc737499d84512690701ed621a6f0172aecf037184ddf0f2453e4053024018e5ab2e30d6d5363b56e8b41509317c99042f517247474ab3abc848e00a07f69c254f46f2a05cf6ed84e5cc906a518fdcfdf2c61ce731f24c5264f1a25fc04934dc28aec112134dd523f70115074ca34e3807aa4cb925147f3a0ce152d323bd8c675ace446d0fd1ae30c4b57f0eb2c23884bc18f0964c0114796c5b6d080c3d89175665fbf63a6381a6a9da39ad070b645c8bb1779506da14439a9f5b5d481954764ea114fac688930bc68534d403cff4210673b6a6ff7ae416b7cd41404c3d3f282fcd193b86d0f54d0006c2a503b40d5c3930da980565b8f9630e9493a79d1c03e74e5f93ac8e4dc1a901ec5e3b3e57049124c7b72ea345aa359e782285d9e6a5c144a378111dd02c40855ff9c2be9b48425cb0b2fd62dc8678fd151121cf26a65e917d65d8e0dacfae108eb5508b601fb8ffa370be1f9a8b749a2d12eeab81f41079de87e2d777994fa4d28188c579ad327f9957fb7bdecec5c680844dd43cb57cf87aeb763c003e65011f73f8c63442df39a92b946a6bd968a1c1e4d5fa7d88476a68bd8e20e5b70a99259c7d3f85fb1b65cd2e93972e6264e74ebf289b8b6979b9b68a85cd5b360c1987f87235c3c845d62489e33acf85d53fa3561fe3a3aee18924588d9c6eba4edb7a4d106b31173e42929f6f0c48c80ce6a72d54eca7c0fe870068b7a7c89c63cdda593f5b32d3cb4ea8a32c39f00ab449155757172d66763ed9527019d6de6c9f2416aa6203f4d11c9ebee1e1d3845099e55504446448027212616167eb36035726daa7698b075286f5379cd3e93cb3e0cf4f9cb8d017facbb5550ed32d5ec5400ae57e47e2bf78d1eaeff9480cc765ceff39db500", + "Name": "nagydani-5-pow0x10001", + "Gas": 87381, + "NoBenchmark": false + } +] \ No newline at end of file diff --git a/actors/evm/shared/Cargo.toml b/actors/evm/shared/Cargo.toml index 7111ad8eb..a0a212fd4 100644 --- a/actors/evm/shared/Cargo.toml +++ b/actors/evm/shared/Cargo.toml @@ -10,5 +10,8 @@ keywords = ["filecoin", "web3", "wasm", "evm"] [dependencies] serde = { version = "1.0.136", features = ["derive"] } -fvm_ipld_encoding = "0.3.3" +fvm_shared = { path = "../../../../ref-fvm/shared", default-features = false } +fil_actors_runtime = { path = "../../../runtime" } +fvm_ipld_encoding = { path = "../../../../ref-fvm/ipld/encoding"} +uint = { version = "0.9.3", default-features = false } hex = "0.4.3" diff --git a/actors/evm/shared/src/address.rs b/actors/evm/shared/src/address.rs index 119314831..a5d013e6f 100644 --- a/actors/evm/shared/src/address.rs +++ b/actors/evm/shared/src/address.rs @@ -1,14 +1,46 @@ +use crate::uints::U256; +use fil_actors_runtime::EAM_ACTOR_ID; use fvm_ipld_encoding::{serde, strict_bytes}; +use fvm_shared::address::Address; +use fvm_shared::ActorID; /// A Filecoin address as represented in the FEVM runtime (also called EVM-form). #[derive(serde::Deserialize, serde::Serialize, PartialEq, Eq, Clone, Copy)] pub struct EthAddress(#[serde(with = "strict_bytes")] pub [u8; 20]); +/// Converts a U256 to an EthAddress by taking the lower 20 bytes. +/// +/// Per the EVM spec, this simply discards the high bytes. +impl From for EthAddress { + fn from(v: U256) -> Self { + let mut bytes = [0u8; 32]; + v.to_big_endian(&mut bytes); + Self(bytes[12..].try_into().unwrap()) + } +} + impl std::fmt::Debug for EthAddress { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(&hex::encode(self.0)) } } + +impl From for Address { + fn from(addr: EthAddress) -> Self { + From::from(&addr) + } +} + +impl From<&EthAddress> for Address { + fn from(addr: &EthAddress) -> Self { + if let Some(id) = addr.as_id() { + Address::new_id(id) + } else { + Address::new_delegated(EAM_ACTOR_ID, addr.as_ref()).unwrap() + } + } +} + impl EthAddress { /// Returns a "null" address. pub const fn null() -> Self { @@ -23,15 +55,124 @@ impl EthAddress { EthAddress(bytes) } + /// Interpret the EVM word as an ID address in EVM-form, and return a Filecoin ID address if + /// that's the case. + /// + /// An ID address starts with 0xff (msb), and contains the u64 in the last 8 bytes. + /// We assert that everything in between are 0x00, otherwise we've gotten an illegal address. + /// + /// 0 1-11 12 + /// 0xff \[0x00...] [id address...] + pub fn as_id(&self) -> Option { + if !self.is_id() { + return None; + } + Some(u64::from_be_bytes(self.0[12..].try_into().unwrap())) + } + + /// Returns this Address as an EVM word. + #[inline] + pub fn as_evm_word(&self) -> U256 { + U256::from_big_endian(&self.0) + } + /// Returns true if this is the null/zero EthAddress. #[inline] pub fn is_null(&self) -> bool { self.0 == [0; 20] } + /// Returns true if the EthAddress refers to an address in the precompile range. + /// [reference](https://github.com/filecoin-project/ref-fvm/issues/1164#issuecomment-1371304676) + #[inline] + pub fn is_precompile(&self) -> bool { + // Exact index is not checked since it is unknown to the EAM what precompiles exist in the EVM actor. + // 0 indexes of both ranges are not assignable as well but are _not_ precompile address. + let [prefix, middle @ .., _index] = self.0; + (prefix == 0xfe || prefix == 0x00) && middle == [0u8; 18] + } + /// Returns true if the EthAddress is an actor ID embedded in an eth address. #[inline] pub fn is_id(&self) -> bool { self.0[0] == 0xff && self.0[1..12].iter().all(|&i| i == 0) } } + +impl AsRef<[u8]> for EthAddress { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +#[cfg(test)] +mod tests { + use super::EthAddress; + use crate::uints::U256; + + // padding (12 bytes) + const TYPE_PADDING: &[u8] = &[0; 12]; + // ID address marker (1 byte) + const ID_ADDRESS_MARKER: &[u8] = &[0xff]; + // ID address marker (1 byte) + const GOOD_ADDRESS_PADDING: &[u8] = + &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; // padding for inner u64 (11 bytes) + + macro_rules! id_address_test { + ($($name:ident: $input:expr => $expectation:expr,)*) => { + $( + #[test] + fn $name() { + let evm_bytes = $input.concat(); + let evm_addr = EthAddress::from(U256::from(evm_bytes.as_slice())); + assert_eq!( + evm_addr.as_id(), + $expectation + ); + + // test inverse conversion, if a valid ID address was supplied + if let Some(fil_id) = $expectation { + assert_eq!(EthAddress::from_id(fil_id), evm_addr); + } + } + )* + }; + } + + id_address_test! { + good_address_1: [ + TYPE_PADDING, + ID_ADDRESS_MARKER, + GOOD_ADDRESS_PADDING, + vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01].as_slice() // ID address (u64 big endian) (8 bytes) + ] => Some(1), + + good_address_2: [ + TYPE_PADDING, + ID_ADDRESS_MARKER, + GOOD_ADDRESS_PADDING, + vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff].as_slice() // ID address (u64 big endian) (8 bytes) + ] => Some(u16::MAX as u64), + + bad_marker: [ + TYPE_PADDING, + &[0xfa], + GOOD_ADDRESS_PADDING, + vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01].as_slice() // ID address (u64 big endian) (8 bytes) + ] => None, + + bad_padding: [ + TYPE_PADDING, + ID_ADDRESS_MARKER, + &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01], // bad padding + vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01].as_slice() // ID address (u64 big endian) (8 bytes) + ] => None, + + bad_marker_and_padding: [ + TYPE_PADDING, + &[0xfa], + &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01], // bad padding + vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01].as_slice() // ID address (u64 big endian) (8 bytes) + ] => None, + } +} diff --git a/actors/evm/shared/src/lib.rs b/actors/evm/shared/src/lib.rs index f74634c7c..f74d12fca 100644 --- a/actors/evm/shared/src/lib.rs +++ b/actors/evm/shared/src/lib.rs @@ -1 +1,2 @@ pub mod address; +pub mod uints; diff --git a/actors/evm/shared/src/uints.rs b/actors/evm/shared/src/uints.rs new file mode 100644 index 000000000..d51e0a306 --- /dev/null +++ b/actors/evm/shared/src/uints.rs @@ -0,0 +1,335 @@ +// to silence construct_uint! clippy warnings +// see https://github.com/paritytech/parity-common/issues/660 +#![allow(clippy::ptr_offset_with_cast, clippy::assign_op_pattern)] + +#[doc(inline)] +pub use uint::byteorder; + +use serde::{Deserialize, Serialize}; +//use substrate_bn::arith; + +use { + fvm_shared::bigint::BigInt, fvm_shared::econ::TokenAmount, std::cmp::Ordering, std::fmt, + uint::construct_uint, +}; + +construct_uint! { pub struct U256(4); } // ethereum word size +construct_uint! { pub struct U512(8); } // used for addmod and mulmod opcodes + +// Convenience method for comparing against a small value. +impl PartialOrd for U256 { + fn partial_cmp(&self, other: &u64) -> Option { + if self.0[3] > 0 || self.0[2] > 0 || self.0[1] > 0 { + Some(Ordering::Greater) + } else { + self.0[0].partial_cmp(other) + } + } +} + +impl PartialEq for U256 { + fn eq(&self, other: &u64) -> bool { + self.0[0] == *other && self.0[1] == 0 && self.0[2] == 0 && self.0[3] == 0 + } +} + +impl U256 { + pub const BITS: u32 = 256; + pub const ZERO: Self = U256::from_u64(0); + pub const ONE: Self = U256::from_u64(1); + pub const I128_MIN: Self = U256([0, 0, 0, i64::MIN as u64]); + + #[inline(always)] + pub const fn from_u128_words(high: u128, low: u128) -> U256 { + U256([low as u64, (low >> u64::BITS) as u64, high as u64, (high >> u64::BITS) as u64]) + } + + #[inline(always)] + pub const fn from_u64(value: u64) -> U256 { + U256([value, 0, 0, 0]) + } + + #[inline(always)] + pub const fn i256_is_negative(&self) -> bool { + (self.0[3] as i64) < 0 + } + + /// turns a i256 value to negative + #[inline(always)] + pub fn i256_neg(&self) -> U256 { + if self.is_zero() { + U256::ZERO + } else { + !*self + U256::ONE + } + } + + #[inline(always)] + pub fn i256_cmp(&self, other: &U256) -> Ordering { + // true > false: + // - true < positive: + match other.i256_is_negative().cmp(&self.i256_is_negative()) { + Ordering::Equal => self.cmp(other), + sign_cmp => sign_cmp, + } + } + + #[inline] + pub fn i256_div(&self, other: &U256) -> U256 { + if self.is_zero() || other.is_zero() { + // EVM defines X/0 to be 0. + return U256::ZERO; + } + + // min-negative-value can't be represented as a positive value, but we don't need to. + // NOTE: we've already checked that 'second' isn't zero above. + if (self, other) == (&U256::I128_MIN, &U256::ONE) { + return U256::I128_MIN; + } + + let mut first = *self; + let mut second = *other; + + // Record and strip the signs. We add them back at the end. + let first_neg = first.i256_is_negative(); + let second_neg = second.i256_is_negative(); + + if first_neg { + first = first.i256_neg() + } + + if second_neg { + second = second.i256_neg() + } + + let d = first / second; + + // Flip the sign back if necessary. + if d.is_zero() || first_neg == second_neg { + d + } else { + d.i256_neg() + } + } + + #[inline] + pub fn i256_mod(&self, other: &U256) -> U256 { + if self.is_zero() || other.is_zero() { + // X % 0 or 0 % X is always 0. + return U256::ZERO; + } + + let mut first = *self; + let mut second = *other; + + // Record and strip the sign. + let negative = first.i256_is_negative(); + if negative { + first = first.i256_neg(); + } + + if second.i256_is_negative() { + second = second.i256_neg() + } + + let r = first % second; + + // Restore the sign. + if negative && !r.is_zero() { + r.i256_neg() + } else { + r + } + } + + pub fn to_bytes(&self) -> [u8; 32] { + let mut buf = [0u8; 32]; + self.to_big_endian(&mut buf); + buf + } + + /// Returns the low 64 bits, saturating the value to u64 max if it is larger + pub fn to_u64_saturating(&self) -> u64 { + if self.bits() > 64 { + u64::MAX + } else { + self.0[0] + } + } +} + +impl U512 { + pub fn low_u256(&self) -> U256 { + let [a, b, c, d, ..] = self.0; + U256([a, b, c, d]) + } +} + +impl From<&TokenAmount> for U256 { + fn from(amount: &TokenAmount) -> U256 { + let (_, bytes) = amount.atto().to_bytes_be(); + U256::from(bytes.as_slice()) + } +} + +impl From for U512 { + fn from(v: U256) -> Self { + let [a, b, c, d] = v.0; + U512([a, b, c, d, 0, 0, 0, 0]) + } +} + +impl From<&U256> for TokenAmount { + fn from(ui: &U256) -> TokenAmount { + let mut bits = [0u8; 32]; + ui.to_big_endian(&mut bits); + TokenAmount::from_atto(BigInt::from_bytes_be(fvm_shared::bigint::Sign::Plus, &bits)) + } +} + +impl Serialize for U256 { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut bytes = [0u8; 32]; + self.to_big_endian(&mut bytes); + serializer.serialize_bytes(zeroless_view(&bytes)) + } +} + +impl<'de> Deserialize<'de> for U256 { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct Visitor; + impl<'de> serde::de::Visitor<'de> for Visitor { + type Value = U256; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "at most 32 bytes") + } + + fn visit_bytes(self, v: &[u8]) -> Result + where + E: serde::de::Error, + { + if v.len() > 32 { + return Err(serde::de::Error::invalid_length(v.len(), &self)); + } + Ok(U256::from_big_endian(v)) + } + } + deserializer.deserialize_bytes(Visitor) + } +} + +fn zeroless_view(v: &impl AsRef<[u8]>) -> &[u8] { + let v = v.as_ref(); + &v[v.iter().take_while(|&&b| b == 0).count()..] +} + +#[cfg(test)] +mod tests { + use fvm_ipld_encoding::{BytesDe, BytesSer, RawBytes}; + + use {super::*, core::num::Wrapping}; + + #[test] + fn div_i256() { + assert_eq!(Wrapping(i8::MIN) / Wrapping(-1), Wrapping(i8::MIN)); + assert_eq!(i8::MAX / -1, -i8::MAX); + + let zero = U256::ZERO; + let one = U256::ONE; + let one_hundred = U256::from(100); + let fifty = U256::from(50); + let two = U256::from(2); + let neg_one_hundred = U256::from(100); + let minus_one = U256::from(1); + let max_value = U256::from(2).pow(255.into()) - 1; + let neg_max_value = U256::from(2).pow(255.into()) - 1; + + assert_eq!(U256::I128_MIN.i256_div(&minus_one), U256::I128_MIN); + assert_eq!(U256::I128_MIN.i256_div(&one), U256::I128_MIN); + assert_eq!(one.i256_div(&U256::I128_MIN), zero); + assert_eq!(max_value.i256_div(&one), max_value); + assert_eq!(max_value.i256_div(&minus_one), neg_max_value); + assert_eq!(one_hundred.i256_div(&minus_one), neg_one_hundred); + assert_eq!(one_hundred.i256_div(&two), fifty); + + assert_eq!(zero.i256_div(&zero), zero); + assert_eq!(one.i256_div(&zero), zero); + assert_eq!(zero.i256_div(&one), zero); + } + + #[test] + fn mod_i256() { + let zero = U256::ZERO; + let one = U256::ONE; + let one_hundred = U256::from(100); + let two = U256::from(2); + let three = U256::from(3); + + let neg_one_hundred = U256::from(100).i256_neg(); + let minus_one = U256::from(1).i256_neg(); + let neg_three = U256::from(3).i256_neg(); + let max_value = U256::from(2).pow(255.into()) - 1; + + // zero + assert_eq!(minus_one.i256_mod(&U256::ZERO), U256::ZERO); + assert_eq!(max_value.i256_mod(&U256::ZERO), U256::ZERO); + assert_eq!(U256::ZERO.i256_mod(&U256::ZERO), U256::ZERO); + + assert_eq!(minus_one.i256_mod(&two), minus_one); + assert_eq!(U256::I128_MIN.i256_mod(&one), 0); + assert_eq!(one.i256_mod(&U256::I128_MIN), one); + assert_eq!(one.i256_mod(&U256::from(i128::MAX)), one); + + assert_eq!(max_value.i256_mod(&minus_one), zero); + assert_eq!(neg_one_hundred.i256_mod(&minus_one), zero); + assert_eq!(one_hundred.i256_mod(&two), zero); + assert_eq!(one_hundred.i256_mod(&neg_three), one); + + assert_eq!(neg_one_hundred.i256_mod(&three), minus_one); + + let a = U256::from(95).i256_neg(); + let b = U256::from(256); + assert_eq!(a % b, U256::from(161)) + } + + #[test] + fn negative_i256() { + assert_eq!(U256::ZERO.i256_neg(), U256::ZERO); + + let one = U256::ONE.i256_neg(); + assert!(one.i256_is_negative()); + + let neg_one = U256::from(&[0xff; 32]); + let pos_one = neg_one.i256_neg(); + assert_eq!(pos_one, U256::ONE); + } + + #[test] + fn u256_serde() { + let encoded = RawBytes::serialize(U256::from(0x4d2)).unwrap(); + let BytesDe(bytes) = encoded.deserialize().unwrap(); + assert_eq!(bytes, &[0x04, 0xd2]); + let decoded: U256 = encoded.deserialize().unwrap(); + assert_eq!(decoded, 0x4d2); + } + + #[test] + fn u256_empty() { + let encoded = RawBytes::serialize(U256::from(0)).unwrap(); + let BytesDe(bytes) = encoded.deserialize().unwrap(); + assert!(bytes.is_empty()); + } + + #[test] + fn u256_overflow() { + let encoded = RawBytes::serialize(BytesSer(&[1; 33])).unwrap(); + encoded.deserialize::().expect_err("should have failed to decode an over-large u256"); + } +} diff --git a/actors/evm/src/ext.rs b/actors/evm/src/ext.rs new file mode 100644 index 000000000..d96f08a25 --- /dev/null +++ b/actors/evm/src/ext.rs @@ -0,0 +1,30 @@ +pub mod eam { + use fil_actors_evm_shared::address::EthAddress; + use fvm_ipld_encoding::{strict_bytes, tuple::*}; + use fvm_shared::address::Address; + + pub const CREATE_METHOD_NUM: u64 = 2; + pub const CREATE2_METHOD_NUM: u64 = 3; + + #[derive(Serialize_tuple, Deserialize_tuple, Clone)] + pub struct CreateParams { + #[serde(with = "strict_bytes")] + pub code: Vec, + pub nonce: u64, + } + + #[derive(Serialize_tuple, Deserialize_tuple, Clone)] + pub struct Create2Params { + #[serde(with = "strict_bytes")] + pub code: Vec, + #[serde(with = "strict_bytes")] + pub salt: [u8; 32], + } + + #[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Copy, PartialEq, Eq)] + pub struct CreateReturn { + pub actor_id: u64, + pub robust_address: Option
, + pub eth_address: EthAddress, + } +} diff --git a/actors/evm/src/interpreter/LICENSE b/actors/evm/src/interpreter/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/actors/evm/src/interpreter/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/actors/evm/src/interpreter/README.md b/actors/evm/src/interpreter/README.md new file mode 100644 index 000000000..f0a5555f4 --- /dev/null +++ b/actors/evm/src/interpreter/README.md @@ -0,0 +1,27 @@ +## Introduction + +This is the EVM interpreter used in the Filecoin network. + +## History + +This interpreter was incubated under [fvm-evm](https://github.com/filecoin-project/fvm-evm/). +It was initially based on [evmodin](https://github.com/vorot93/evmodin) (whose [LICENSE](./LICENSE) has been transferred here), +but has diverged significantly. + +## Divergences + +This is a non-comprehensive list. + +- Because this interpreter does not service an Ethereum network, we were able to remove historical baggage and then + tracking of which opcodes and precompiles were introduced at which forks. This interpreter supports the Berlin hardfork. +- Removed support for continuations. We don't expect to use this feature in FVM. +- Removed support for tracing. We may want to re-introduce this at some point proxying over to the debug::log syscall. +- All instructions under instructions/ have been `#[inlined]`. +- The Host trait has been removed and substituted by a System concrete type that uses the FVM SDK (and thus depends + on the actor Wasm sandbox). We will likely need to restore this trait for unit testing purposes. +- The Memory is now backed by BytesVec instead of a Vec; it exposes a method to grow it, but we need to check that it's + being called from every possible point. +- `Message#code_address` has been removed; we may need to reintroduce when we start handling delegate call. + Bytecode processing has lost features, e.g. code padding (was it an implementation detail?), only-once jumpdest table + derivation and persistence, etc. This is connected with the removal of continuations and other features. +- Many code layout/structure changes and refactors. \ No newline at end of file diff --git a/actors/evm/src/interpreter/bytecode.rs b/actors/evm/src/interpreter/bytecode.rs new file mode 100644 index 000000000..a2ba298e8 --- /dev/null +++ b/actors/evm/src/interpreter/bytecode.rs @@ -0,0 +1,52 @@ +use std::ops::Deref; + +use super::opcodes; + +#[derive(Clone, Debug)] +pub struct Bytecode { + code: Vec, + jumpdest: Vec, +} + +impl Bytecode { + pub fn new(bytecode: Vec) -> Self { + // only jumps to those addresses are valid. This is a security + // feature by EVM to disallow jumps to arbitary code addresses. + let mut jumpdest = vec![false; bytecode.len()]; + let mut i = 0; + while i < bytecode.len() { + if bytecode[i] == opcodes::JUMPDEST as u8 { + jumpdest[i] = true; + i += 1; + } else if bytecode[i] >= opcodes::PUSH1 && bytecode[i] <= opcodes::PUSH32 { + i += (bytecode[i] - opcodes::PUSH1) as usize + 2; + } else { + i += 1; + } + } + + Self { code: bytecode, jumpdest } + } + + /// Checks if the EVM is allowed to jump to this location. + /// + /// This location must begin with a JUMPDEST opcode that + /// marks a valid jump destination + pub fn valid_jump_destination(&self, offset: usize) -> bool { + offset < self.jumpdest.len() && self.jumpdest[offset] + } +} + +impl Deref for Bytecode { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + &self.code + } +} + +impl AsRef<[u8]> for Bytecode { + fn as_ref(&self) -> &[u8] { + &self.code + } +} diff --git a/actors/evm/src/interpreter/execution.rs b/actors/evm/src/interpreter/execution.rs new file mode 100644 index 000000000..5fcc105fc --- /dev/null +++ b/actors/evm/src/interpreter/execution.rs @@ -0,0 +1,514 @@ +use fil_actors_evm_shared::address::EthAddress; +use fil_actors_runtime::ActorError; +use fvm_shared::econ::TokenAmount; + +use { + super::instructions, + super::memory::Memory, + super::stack::Stack, + super::{Bytecode, Output, System}, + fil_actors_runtime::runtime::Runtime, +}; + +/// EVM execution runtime. +#[derive(Clone, Debug)] +pub struct ExecutionState { + pub stack: Stack, + pub memory: Memory, + pub input_data: Vec, + pub return_data: Vec, + /// The EVM address of the caller. + pub caller: EthAddress, + /// The EVM address of the receiver. + pub receiver: EthAddress, + /// The value received in this call. + pub value_received: TokenAmount, +} + +impl ExecutionState { + pub fn new( + caller: EthAddress, + receiver: EthAddress, + value_received: TokenAmount, + input_data: Vec, + ) -> Self { + Self { + stack: Stack::new(), + memory: Memory::default(), + input_data, + return_data: Default::default(), + caller, + receiver, + value_received, + } + } +} + +pub struct Machine<'r, 'a, RT: Runtime + 'a> { + pub system: &'r mut System<'a, RT>, + pub state: &'r mut ExecutionState, + pub bytecode: &'r Bytecode, + pub pc: usize, + pub output: Output, +} + +macro_rules! def_opcodes { + ($($code:literal: $op:ident,)*) => { + pub(crate) const fn jumptable<'r, 'a, RT: Runtime>() -> [Instruction<'r, 'a, RT>; 256] { + def_ins_raw! { + UNDEFINED(_m) { + Err(ActorError::unchecked( + crate::EVM_CONTRACT_UNDEFINED_INSTRUCTION, + "undefined instruction".into() + )) + } + } + $(def_ins_raw! { + $op (m) { + instructions::$op(m) + } + })* + + let mut table: [Instruction<'r, 'a, RT>; 256] = [UNDEFINED; 256]; + $(table[$code] = $op;)* + table + } + $(pub const $op: u8 = $code;)* + } +} + +macro_rules! def_ins_raw { + ($ins:ident ($arg:ident) $body:block) => { + #[allow(non_snake_case)] + unsafe fn $ins<'r, 'a, RT: Runtime>(p: *mut Machine<'r, 'a, RT>) -> Result<(), ActorError> { + // SAFETY: macro ensures that mut pointer is taken directly from a mutable borrow, used + // once, then goes out of scope immediately after + let $arg: &mut Machine<'r, 'a, RT> = &mut *p; + $body + } + }; +} + +pub mod opcodes { + use super::instructions; + use super::Machine; + use fil_actors_runtime::runtime::Runtime; + use fil_actors_runtime::ActorError; + + pub(crate) type Instruction<'r, 'a, RT> = + unsafe fn(*mut Machine<'r, 'a, RT>) -> Result<(), ActorError>; + + def_opcodes! { + 0x00: STOP, + 0x01: ADD, + 0x02: MUL, + 0x03: SUB, + 0x04: DIV, + 0x05: SDIV, + 0x06: MOD, + 0x07: SMOD, + 0x08: ADDMOD, + 0x09: MULMOD, + 0x0a: EXP, + 0x0b: SIGNEXTEND, + 0x10: LT, + 0x11: GT, + 0x12: SLT, + 0x13: SGT, + 0x14: EQ, + 0x15: ISZERO, + 0x16: AND, + 0x17: OR, + 0x18: XOR, + 0x19: NOT, + 0x1a: BYTE, + 0x1b: SHL, + 0x1c: SHR, + 0x1d: SAR, + 0x20: KECCAK256, + 0x30: ADDRESS, + 0x31: BALANCE, + 0x32: ORIGIN, + 0x33: CALLER, + 0x34: CALLVALUE, + 0x35: CALLDATALOAD, + 0x36: CALLDATASIZE, + 0x37: CALLDATACOPY, + 0x38: CODESIZE, + 0x39: CODECOPY, + 0x3a: GASPRICE, + 0x3b: EXTCODESIZE, + 0x3c: EXTCODECOPY, + 0x3d: RETURNDATASIZE, + 0x3e: RETURNDATACOPY, + 0x3f: EXTCODEHASH, + 0x40: BLOCKHASH, + 0x41: COINBASE, + 0x42: TIMESTAMP, + 0x43: NUMBER, + 0x44: PREVRANDAO, + 0x45: GASLIMIT, + 0x46: CHAINID, + 0x47: SELFBALANCE, + 0x48: BASEFEE, + 0x50: POP, + 0x51: MLOAD, + 0x52: MSTORE, + 0x53: MSTORE8, + 0x54: SLOAD, + 0x55: SSTORE, + 0x56: JUMP, + 0x57: JUMPI, + 0x58: PC, + 0x59: MSIZE, + 0x5a: GAS, + 0x5b: JUMPDEST, + 0x5F: PUSH0, + 0x60: PUSH1, + 0x61: PUSH2, + 0x62: PUSH3, + 0x63: PUSH4, + 0x64: PUSH5, + 0x65: PUSH6, + 0x66: PUSH7, + 0x67: PUSH8, + 0x68: PUSH9, + 0x69: PUSH10, + 0x6a: PUSH11, + 0x6b: PUSH12, + 0x6c: PUSH13, + 0x6d: PUSH14, + 0x6e: PUSH15, + 0x6f: PUSH16, + 0x70: PUSH17, + 0x71: PUSH18, + 0x72: PUSH19, + 0x73: PUSH20, + 0x74: PUSH21, + 0x75: PUSH22, + 0x76: PUSH23, + 0x77: PUSH24, + 0x78: PUSH25, + 0x79: PUSH26, + 0x7a: PUSH27, + 0x7b: PUSH28, + 0x7c: PUSH29, + 0x7d: PUSH30, + 0x7e: PUSH31, + 0x7f: PUSH32, + 0x80: DUP1, + 0x81: DUP2, + 0x82: DUP3, + 0x83: DUP4, + 0x84: DUP5, + 0x85: DUP6, + 0x86: DUP7, + 0x87: DUP8, + 0x88: DUP9, + 0x89: DUP10, + 0x8a: DUP11, + 0x8b: DUP12, + 0x8c: DUP13, + 0x8d: DUP14, + 0x8e: DUP15, + 0x8f: DUP16, + 0x90: SWAP1, + 0x91: SWAP2, + 0x92: SWAP3, + 0x93: SWAP4, + 0x94: SWAP5, + 0x95: SWAP6, + 0x96: SWAP7, + 0x97: SWAP8, + 0x98: SWAP9, + 0x99: SWAP10, + 0x9a: SWAP11, + 0x9b: SWAP12, + 0x9c: SWAP13, + 0x9d: SWAP14, + 0x9e: SWAP15, + 0x9f: SWAP16, + 0xa0: LOG0, + 0xa1: LOG1, + 0xa2: LOG2, + 0xa3: LOG3, + 0xa4: LOG4, + // 0xEF Reserved for EIP-3541 + 0xf0: CREATE, + 0xf1: CALL, + 0xf3: RETURN, + 0xf4: DELEGATECALL, + 0xf5: CREATE2, + 0xfa: STATICCALL, + 0xfd: REVERT, + 0xfe: INVALID, + 0xff: SELFDESTRUCT, + } +} + +impl<'r, 'a, RT: Runtime + 'r> Machine<'r, 'a, RT> { + pub fn new( + system: &'r mut System<'a, RT>, + state: &'r mut ExecutionState, + bytecode: &'r Bytecode, + ) -> Self { + Machine { system, state, bytecode, pc: 0, output: Output::default() } + } + + pub fn execute(mut self) -> Result { + while self.pc < self.bytecode.len() { + // This is faster than the question mark operator, and speed counts here. + #[allow(clippy::question_mark)] + if let Err(e) = self.step() { + return Err(e.wrap(format!("ABORT(pc={})", self.pc))); + } + } + + Ok(self.output) + } + + #[inline(always)] + // Note: pub only for unit test steps. + pub(crate) fn step(&mut self) -> Result<(), ActorError> { + let op = self.bytecode[self.pc]; + unsafe { Self::JMPTABLE[op as usize](self) } + } + + const JMPTABLE: [opcodes::Instruction<'r, 'a, RT>; 256] = opcodes::jumptable::<'r, 'a, RT>(); +} + +pub fn execute( + bytecode: &Bytecode, + runtime: &mut ExecutionState, + system: &mut System, +) -> Result { + Machine::new(system, runtime, bytecode).execute() +} + +#[cfg(test)] +mod tests { + use crate::evm_unit_test; + + macro_rules! check_underflow_err { + ($($ins:ident,)*) => { + $(do_check_underflow_err!($ins);)* + } + } + + macro_rules! check_underflow_none { + ($($ins:ident,)*) => { + $(do_check_underflow_none!($ins);)* + } + } + + macro_rules! do_check_underflow_err { + ($ins:ident) => { + { + evm_unit_test! { + (m) { $ins; } + let result = m.step(); + assert!(result.is_err(), stringify!($ins)); + assert_eq!(result.unwrap_err().exit_code(), crate::EVM_CONTRACT_STACK_UNDERFLOW, stringify!($ins)); + }; + } + } + } + + macro_rules! do_check_underflow_none { + ($ins:ident) => {{ + evm_unit_test! { + (m) { $ins; } + let result = m.step(); + assert!(result.is_ok(), stringify!($ins)); + }; + }}; + } + + #[test] + fn test_execution_underflow() { + check_underflow_err!( + ADD, + MUL, + SUB, + DIV, + SDIV, + MOD, + SMOD, + ADDMOD, + MULMOD, + EXP, + SIGNEXTEND, + LT, + GT, + SLT, + SGT, + EQ, + ISZERO, + AND, + OR, + XOR, + NOT, + BYTE, + SHL, + SHR, + SAR, + DUP1, + DUP2, + DUP3, + DUP4, + DUP5, + DUP6, + DUP7, + DUP8, + DUP9, + DUP10, + DUP11, + DUP12, + DUP13, + DUP14, + DUP15, + DUP16, + SWAP1, + SWAP2, + SWAP3, + SWAP4, + SWAP5, + SWAP6, + SWAP7, + SWAP8, + SWAP9, + SWAP10, + SWAP11, + SWAP12, + SWAP13, + SWAP14, + SWAP15, + SWAP16, + POP, + KECCAK256, + BALANCE, + CALLDATALOAD, + CALLDATACOPY, + EXTCODESIZE, + EXTCODECOPY, + EXTCODEHASH, + RETURNDATACOPY, + BLOCKHASH, + MLOAD, + MSTORE, + MSTORE8, + SLOAD, + SSTORE, + LOG0, + LOG1, + LOG2, + LOG3, + LOG4, + CALL, + DELEGATECALL, + STATICCALL, + CODECOPY, + CREATE, + CREATE2, + RETURN, + REVERT, + SELFDESTRUCT, + JUMP, + JUMPI, + ); + + check_underflow_none!( + PUSH0, + PUSH1, + PUSH2, + PUSH3, + PUSH4, + PUSH5, + PUSH6, + PUSH7, + PUSH8, + PUSH9, + PUSH10, + PUSH11, + PUSH12, + PUSH13, + PUSH14, + PUSH15, + PUSH16, + PUSH17, + PUSH18, + PUSH19, + PUSH20, + PUSH21, + PUSH22, + PUSH23, + PUSH24, + PUSH25, + PUSH26, + PUSH27, + PUSH28, + PUSH29, + PUSH30, + PUSH31, + PUSH32, + ADDRESS, + ORIGIN, + CALLER, + CALLVALUE, + CALLDATASIZE, + GASPRICE, + RETURNDATASIZE, + COINBASE, + TIMESTAMP, + NUMBER, + GASLIMIT, + CHAINID, + BASEFEE, + SELFBALANCE, + MSIZE, + CODESIZE, + JUMPDEST, + STOP, + PC, + ); + + // manual checks + { + evm_unit_test! { + (rt) { + let epoch = 1234; + rt.set_epoch(epoch); + rt.expect_get_randomness_from_beacon( + fil_actors_runtime::runtime::DomainSeparationTag::EvmPrevRandao, + epoch, + Vec::from(*b"prevrandao"), + [0xff; 32] + ); + } + (m) { PREVRANDAO; } + let result = m.step(); + assert!(result.is_ok()); + }; + } + + { + evm_unit_test! { + (rt) { + rt.expect_gas_available(1234); + } + (m) { GAS; } + let result = m.step(); + assert!(result.is_ok()); + }; + } + + { + evm_unit_test! { + (m) { INVALID; } + let result = m.step(); + assert!(result.is_err()); + assert_eq!(result.unwrap_err().exit_code(), crate::EVM_CONTRACT_INVALID_INSTRUCTION); + }; + } + } +} diff --git a/actors/evm/src/interpreter/instructions/arithmetic.rs b/actors/evm/src/interpreter/instructions/arithmetic.rs new file mode 100644 index 000000000..77971e27d --- /dev/null +++ b/actors/evm/src/interpreter/instructions/arithmetic.rs @@ -0,0 +1,287 @@ +use fil_actors_evm_shared::uints::{U256, U512}; + +#[inline] +pub fn add(a: U256, b: U256) -> U256 { + a.overflowing_add(b).0 +} + +#[inline] +pub fn mul(a: U256, b: U256) -> U256 { + a.overflowing_mul(b).0 +} + +#[inline] +pub fn sub(a: U256, b: U256) -> U256 { + a.overflowing_sub(b).0 +} + +#[inline] +pub fn div(a: U256, b: U256) -> U256 { + // TODO shortcut optimizations from go's lib (our doesn't) + // https://github.com/holiman/uint256/blob/6f8ccba90ce6cba9727ad5aa26bb925a25b50d29/uint256.go#L544 + if !b.is_zero() { + a / b + } else { + b + } +} + +#[inline] +pub fn sdiv(a: U256, b: U256) -> U256 { + a.i256_div(&b) +} + +#[inline] +pub fn modulo(a: U256, b: U256) -> U256 { + if !b.is_zero() { + a % b + } else { + b + } +} + +#[inline] +pub fn smod(a: U256, b: U256) -> U256 { + a.i256_mod(&b) +} + +#[inline] +pub fn addmod(a: U256, b: U256, c: U256) -> U256 { + if !c.is_zero() { + let al: U512 = a.into(); + let bl: U512 = b.into(); + let cl: U512 = c.into(); + + ((al + bl) % cl).low_u256() + } else { + c + } +} + +#[inline] +pub fn mulmod(a: U256, b: U256, c: U256) -> U256 { + if !c.is_zero() { + let al: U512 = a.into(); + let bl: U512 = b.into(); + let cl: U512 = c.into(); + ((al * bl) % cl).low_u256() + } else { + c + } +} + +#[inline] +pub fn signextend(a: U256, b: U256) -> U256 { + if a < 32 { + let bit_index = 8 * a.low_u32() + 7; + let mask = U256::MAX >> (U256::BITS - bit_index); + if b.bit(bit_index as usize) { + b | !mask + } else { + b & mask + } + } else { + b + } +} + +#[inline] +pub fn exp(mut base: U256, power: U256) -> U256 { + let mut v = U256::ONE; + + // First, compute the number of remaining significant bits. + let mut remaining_bits = U256::BITS - power.leading_zeros(); + + // Word by word, least significant to most. + for mut word in power.0 { + // While we have bits left... + for _ in 0..u64::BITS.min(remaining_bits) { + if (word & 1) != 0 { + v = v.overflowing_mul(base).0; + } + word >>= 1; + base = base.overflowing_mul(base).0; + } + remaining_bits = remaining_bits.saturating_sub(u64::BITS); + } + + v +} + +#[cfg(test)] +mod test { + mod basic { + use super::super::*; + use fil_actors_evm_shared::uints::U256; + + #[test] + fn test_addmod() { + assert_eq!(addmod(4.into(), 5.into(), 3.into()), 0, "4 + 5 % 3 = 0"); + assert_eq!(addmod(0.into(), 5.into(), 3.into()), 2, "0 + 5 % 3 = 2"); + assert_eq!(addmod(0.into(), 0.into(), 3.into()), 0, "0 + 0 % 3 = 0"); + // per evm, mod 0 is 0 + assert_eq!(addmod(1.into(), 2.into(), 0.into()), 0, "1 + 2 % 0 = 0"); + + assert_eq!(addmod(U256::MAX, U256::MAX, 4.into()), 2, "max + max % 4 = 2"); + } + + #[test] + fn test_mulmod() { + assert_eq!(mulmod(4.into(), 5.into(), 3.into()), 2, "4 * 5 % 3 = 2"); + assert_eq!(mulmod(0.into(), 5.into(), 3.into()), 0, "0 * 5 % 3 = 0"); + assert_eq!(mulmod(0.into(), 0.into(), 3.into()), 0, "0 * 0 % 3 = 0"); + // per evm, mod 0 is 0 + assert_eq!(mulmod(1.into(), 2.into(), 0.into()), 0, "1 * 2 % 0 = 0"); + + assert_eq!(mulmod(U256::MAX, U256::MAX, 2.into()), 1, "max * max % 2 = 1"); + } + + #[test] + fn test_add() { + assert_eq!(add(0.into(), 0.into()), 0, "add nothing to nothing"); + // does "math" on all limbs, so it is different than above + assert_eq!( + add(U256::max_value(), 0.into()), + U256::max_value(), + "add nothing to max value" + ); + assert_eq!(add(2.into(), 2.into()), 4, "2 plus 2 equals 5 (???)"); + assert_eq!( + add((u64::MAX).into(), 32.into()), + U256::from(u64::MAX as u128 + 32), + "add 32 past a single (u64) limb of u256" + ); + // wrap to zero + assert_eq!(add(U256::max_value(), 1.into()), 0, "overflow by one"); + // wrap all limbs + assert_eq!( + add(U256::max_value(), U256::max_value()), + U256::max_value() - 1, + "overflow by max, should be 2^256-1" + ); + } + + #[test] + fn test_mul() { + assert_eq!(mul(0.into(), 0.into()), 0, "multiply nothing by nothing"); + assert_eq!(mul(2.into(), 3.into()), 6, "multiply 2 by 3"); + assert_eq!( + mul(u64::MAX.into(), 2.into()), + U256::from((u64::MAX as u128) * 2), + "2^64 x 2" + ); + } + + #[test] + fn test_sub() { + assert_eq!(sub(0.into(), 0.into()), 0, "subtract nothing by nothing"); + assert_eq!(sub(2.into(), 1.into()), 1, "subtract 2 by 1"); + assert_eq!( + sub(((u64::MAX as u128) + 32).into(), 64.into()), + u64::MAX - 32, + "subtract 64 from a value 32 over a single limb" + ); + // wrap to max + assert_eq!(sub(0.into(), 1.into()), U256::max_value(), "wrap around to max by one"); + // wrap all limbs + assert_eq!(sub(U256::max_value(), U256::max_value()), 0, "wrap around zero by 2^256"); + } + + #[test] + fn test_div() { + assert_eq!(div(0.into(), 0.into()), 0, "divide nothing by nothing (yes)"); + assert_eq!(div(4.into(), 1.into()), 4, "divide 4 by 1"); + assert_eq!( + div((u128::MAX).into(), 2.into()), + U256::from(u128::MAX / 2), + "divide 2^128 by 2 (uses >1 limb)" + ); + } + + #[test] + fn test_modulo() { + assert_eq!(modulo(0.into(), 0.into()), 0, "nothing mod nothing is nothing"); + assert_eq!(modulo(4.into(), 1.into()), 0, "4 mod 1 is 0"); + assert_eq!( + modulo((u128::MAX).into(), 2.into()), + U256::from(u128::MAX % 2), + "2^128 mod 2" + ); + + assert_eq!( + modulo((u128::MAX).into(), 7.into()), + U256::from(u128::MAX % 7), + "2^128 mod 7" + ); + } + + #[test] + fn test_signextend() { + macro_rules! assert_exp { + ($num:expr, $byte:expr, $result:expr) => { + let res: U256 = $result.into(); + assert_eq!(res, signextend(($byte).into(), ($num).into())); + }; + } + assert_exp!(0xff, 0, U256::MAX); + assert_exp!(0xff, 1, 0xff); + assert_exp!(0xf0, 0, !U256::from_u64(0x0f)); + // Large + assert_exp!( + U256::from_u128_words(0x82, 0x1), + 16, + U256::from_u128_words((u128::MAX ^ 0xff) | 0x82, 0x1) + ); + assert_exp!(U256::from_u128_words(0x82, 0x1), 15, U256::from_u128_words(0x0, 0x1)); + assert_exp!(U256::from_u128_words(0x82, 0x1), 17, U256::from_u128_words(0x82, 0x1)); + // Not At Boundary + assert_exp!(U256::from_u128_words(0x62, 0x1), 16, U256::from_u128_words(0x62, 0x1)); + } + + #[test] + fn test_exp() { + macro_rules! assert_exp { + ($base:expr, $exp:expr, $result:expr) => { + let res: U256 = $result.into(); + assert_eq!(res, exp(($base).into(), ($exp).into())); + }; + } + + // Basic tests. + for (base, exp) in + [(0u64, 0u32), (0, 1), (1, 0), (1, 10), (10, 1), (10, 0), (0, 10), (10, 10)] + { + assert_exp!(base, exp, base.pow(exp)); + } + + // BIG no-op tests + assert_exp!(U256::from_u128_words(1, 0), 1, U256::from_u128_words(1, 0)); + assert_exp!(U256::from_u128_words(1, 0), 0, 1); + + // BIG actual tests + assert_exp!( + U256::from_u128_words(0, 1 << 65), + 2, + U256::from_u128_words(4 /* 65 * 2 = 128 + 4 */, 0) + ); + + // Check overflow. + assert_exp!(100, U256::from_u128_words(1, 0), U256::ZERO); + assert_exp!(U256::from_u128_words(1, 0), 100, U256::ZERO); + // Check big wrapping. + assert_exp!( + 123, + U256::from_u128_words(0, 123 << 64), + U256::from_u128_words( + 0x9c4a2f94642e820e0f1d7d4208d629d8, + 0xd9ae51d86b0ede140000000000000001 + ) + ); + assert_exp!( + U256::from_u128_words(0, 1 << 66) - U256::ONE, + U256::from_u128_words(0, 1 << 67) - U256::ONE, + U256::from_u128_words(0x80000000000000000f, 0xfffffffffffffffbffffffffffffffff) + ); + } + } +} diff --git a/actors/evm/src/interpreter/instructions/bitwise.rs b/actors/evm/src/interpreter/instructions/bitwise.rs new file mode 100644 index 000000000..b23452cbb --- /dev/null +++ b/actors/evm/src/interpreter/instructions/bitwise.rs @@ -0,0 +1,142 @@ +use fil_actors_evm_shared::uints::U256; + +#[inline] +pub fn byte(i: U256, x: U256) -> U256 { + if i >= 32 { + U256::ZERO + } else { + U256::from_u64(x.byte(31 - i.low_u64() as usize) as u64) + } +} + +#[inline] +pub fn shl(shift: U256, value: U256) -> U256 { + if value.is_zero() || shift >= 256 { + U256::ZERO + } else { + value << shift + } +} + +#[inline] +pub fn shr(shift: U256, value: U256) -> U256 { + if value.is_zero() || shift >= 256 { + U256::ZERO + } else { + value >> shift + } +} + +#[inline] +pub fn sar(shift: U256, mut value: U256) -> U256 { + let negative = value.i256_is_negative(); + if negative { + value = value.i256_neg(); + } + + if value.is_zero() || shift >= 256 { + if negative { + // value is < 0, pushing U256::MAX (== -1) + U256::MAX + } else { + // value is >= 0, pushing 0 + U256::ZERO + } + } else { + let shift = shift.low_u32(); + + if negative { + let shifted = + (value.overflowing_sub(U256::ONE).0 >> shift).overflowing_add(U256::ONE).0; + shifted.i256_neg() + } else { + value >> shift + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_shl() { + // Basic shift + assert_eq!(shl(U256::from(2), U256::from(13)), U256::from(52)); + + // 0/1 shifts. + assert_eq!(shl(U256::ONE, U256::ONE), U256::from(2)); + assert_eq!(shl(U256::ONE, U256::ZERO), U256::ZERO); + assert_eq!(shl(U256::ZERO, U256::ONE), U256::ONE); + assert_eq!(shl(U256::ZERO, U256::ZERO), U256::ZERO); + + // shift max bits + assert_eq!(shl(U256::ONE, U256::MAX), U256::MAX - U256::ONE); + assert_eq!(shl(U256::from(2), U256::MAX), U256::MAX - U256::from(3)); + + // shift by max + assert_eq!(shl(U256::from(255), U256::MAX), U256::from_u128_words(i128::MIN as u128, 0)); + assert_eq!(shl(U256::from(256), U256::MAX), U256::ZERO); + assert_eq!(shl(U256::from(257), U256::MAX), U256::ZERO); + } + + #[test] + fn test_shr() { + // Basic shift + assert_eq!(shr(U256::from(2), U256::from(13)), U256::from(3)); + + // 0/1 shifts. + assert_eq!(shr(U256::ONE, U256::ONE), U256::ZERO); + assert_eq!(shr(U256::ONE, U256::ZERO), U256::ZERO); + assert_eq!(shr(U256::ZERO, U256::ONE), U256::ONE); + assert_eq!(shr(U256::ZERO, U256::ZERO), U256::ZERO); + + // shift max + assert_eq!(shr(U256::from(255), U256::MAX), U256::ONE); + assert_eq!(shr(U256::from(256), U256::MAX), U256::ZERO); + assert_eq!(shr(U256::from(257), U256::MAX), U256::ZERO); + } + + #[test] + fn test_sar() { + let pos_max = shr(U256::ONE, U256::MAX); + + // Basic shift + assert_eq!(sar(U256::from(2), U256::from(13)), U256::from(3)); + assert_eq!(sar(U256::from(2), U256::from(13).i256_neg()), U256::from(4).i256_neg()); + + // 0/1 shifts. + assert_eq!(sar(U256::ONE, U256::ONE), U256::ZERO); + assert_eq!(sar(U256::ONE, U256::ZERO), U256::ZERO); + assert_eq!(sar(U256::ZERO, U256::ONE), U256::ONE); + assert_eq!(sar(U256::ZERO, U256::ZERO), U256::ZERO); + + // shift max negative + assert_eq!(sar(U256::from(255), U256::MAX), U256::MAX); // sign extends. + assert_eq!(sar(U256::from(256), U256::MAX), U256::MAX); + assert_eq!(sar(U256::from(257), U256::MAX), U256::MAX); + + // shift max positive. + assert_eq!(sar(U256::from(254), pos_max), U256::ONE); + assert_eq!(sar(U256::from(255), pos_max), U256::ZERO); + assert_eq!(sar(U256::from(256), pos_max), U256::ZERO); + assert_eq!(sar(U256::from(257), pos_max), U256::ZERO); + } + + #[test] + fn test_instruction_byte() { + let value = U256::from_big_endian(&(1u8..=32u8).map(|x| 5 * x).collect::>()); + + for i in 0u16..32 { + let result = byte(U256::from(i), value); + + assert_eq!(result, U256::from(5 * (i + 1))); + } + + let result = byte(U256::from(100u128), value); + assert_eq!(result, U256::zero()); + + let result = byte(U256::from_u128_words(1, 0), value); + assert_eq!(result, U256::zero()); + } +} diff --git a/actors/evm/src/interpreter/instructions/boolean.rs b/actors/evm/src/interpreter/instructions/boolean.rs new file mode 100644 index 000000000..72f4e55d0 --- /dev/null +++ b/actors/evm/src/interpreter/instructions/boolean.rs @@ -0,0 +1,208 @@ +use std::cmp::Ordering; + +use fil_actors_evm_shared::uints::U256; + +#[inline] +pub fn lt(a: U256, b: U256) -> U256 { + U256::from_u64((a < b).into()) +} + +#[inline] +pub fn gt(a: U256, b: U256) -> U256 { + U256::from_u64((a > b).into()) +} + +#[inline] +pub(crate) fn slt(a: U256, b: U256) -> U256 { + U256::from_u64((a.i256_cmp(&b) == Ordering::Less).into()) +} + +#[inline] +pub(crate) fn sgt(a: U256, b: U256) -> U256 { + U256::from_u64((a.i256_cmp(&b) == Ordering::Greater).into()) +} + +#[inline] +pub fn eq(a: U256, b: U256) -> U256 { + U256::from_u64((a == b).into()) +} + +#[inline] +pub fn iszero(a: U256) -> U256 { + U256::from_u64(a.is_zero().into()) +} + +#[inline] +pub(crate) fn and(a: U256, b: U256) -> U256 { + a & b +} + +#[inline] +pub(crate) fn or(a: U256, b: U256) -> U256 { + a | b +} + +#[inline] +pub(crate) fn xor(a: U256, b: U256) -> U256 { + a ^ b +} + +#[inline] +pub(crate) fn not(v: U256) -> U256 { + !v +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_less_than() { + for i in 0..u8::MAX { + for j in 0..u8::MAX { + let a = U256::from(i); + let b = U256::from(j); + assert_eq!(lt(a, b), U256::from((i < j) as u8)) + } + } + + assert_eq!(lt(U256::ZERO, U256::ZERO), U256::ZERO); + assert_eq!(lt(U256::ZERO, U256::MAX), U256::ONE); + assert_eq!(lt(U256::MAX, U256::ZERO), U256::ZERO); + assert_eq!(lt(U256::MAX, U256::MAX), U256::ZERO); + } + + #[test] + fn test_greater_than() { + for i in 0..u8::MAX { + for j in 0..u8::MAX { + let a = U256::from(i); + let b = U256::from(j); + assert_eq!(gt(a, b), U256::from((i > j) as u8)) + } + } + + assert_eq!(gt(U256::ZERO, U256::ZERO), U256::ZERO); + assert_eq!(gt(U256::ZERO, U256::MAX), U256::ZERO); + assert_eq!(gt(U256::MAX, U256::ZERO), U256::ONE); + assert_eq!(gt(U256::MAX, U256::MAX), U256::ZERO); + } + + #[test] + fn test_sign_less_than() { + let first = i8::MIN + 1; + let last = i8::MAX; + for i in first..=last { + for j in first..=last { + let a = if i.is_negative() { U256::from(-i).i256_neg() } else { U256::from(i) }; + + let b = if j.is_negative() { U256::from(-j).i256_neg() } else { U256::from(j) }; + + assert_eq!(slt(a, b), U256::from((i < j) as u8)) + } + } + + assert_eq!(slt(U256::I128_MIN, U256::ZERO), U256::ONE); + assert_eq!(slt(U256::ZERO, U256::from(i128::MAX).i256_neg()), U256::ZERO); + assert_eq!(slt(U256::ZERO, U256::from(i128::MAX)), U256::ONE); + } + + #[test] + fn test_sign_greater_than() { + let first = i8::MIN + 1; + let last = i8::MAX; + for i in first..=last { + for j in first..=last { + let a = if i.is_negative() { U256::from(-i).i256_neg() } else { U256::from(i) }; + + let b = if j.is_negative() { U256::from(-j).i256_neg() } else { U256::from(j) }; + + assert_eq!(sgt(a, b), U256::from((i > j) as u8)) + } + } + + assert_eq!(sgt(U256::I128_MIN, U256::ZERO), U256::ZERO); + assert_eq!(sgt(U256::ZERO, U256::from(i128::MAX).i256_neg()), U256::ONE); + assert_eq!(sgt(U256::ZERO, U256::from(i128::MAX)), U256::ZERO); + } + + #[test] + fn test_eq() { + assert_eq!(eq(U256::I128_MIN, U256::ZERO), U256::ZERO); + assert_eq!(eq(U256::ZERO, U256::from(i128::MAX).i256_neg()), U256::ZERO); + assert_eq!(eq(U256::ZERO, U256::from(i128::MAX)), U256::ZERO); + assert_eq!(eq(U256::ZERO, (U256::MAX).i256_neg()), U256::ZERO); + assert_eq!(eq(U256::ZERO, U256::ZERO), U256::ONE); + assert_eq!(eq(U256::MAX, U256::MAX), U256::ONE); + } + + #[test] + fn test_iszero() { + assert_eq!(iszero(U256::I128_MIN), U256::ZERO); + assert_eq!(iszero(U256::MAX), U256::ZERO); + assert_eq!(iszero(U256::MAX.i256_neg()), U256::ZERO); + assert_eq!(iszero(U256::ONE), U256::ZERO); + assert_eq!(iszero(U256::from(12345)), U256::ZERO); + assert_eq!(iszero(U256::ZERO), U256::ONE); + } + + #[test] + fn test_and() { + for i in 0..u8::MAX { + for j in 0..u8::MAX { + let a = U256::from(i); + let b = U256::from(j); + assert_eq!(and(a, b), U256::from(i & j)) + } + } + + assert_eq!(and(U256::MAX, U256::ZERO), U256::ZERO); + assert_eq!(and(U256::ZERO, U256::MAX), U256::ZERO); + assert_eq!(and(U256::MAX, U256::MAX), U256::MAX); + assert_eq!(and(U256::ZERO, U256::ZERO), U256::ZERO); + } + + #[test] + fn test_or() { + for i in 0..u8::MAX { + for j in 0..u8::MAX { + let a = U256::from(i); + let b = U256::from(j); + assert_eq!(or(a, b), U256::from(i | j)) + } + } + + assert_eq!(or(U256::MAX, U256::ZERO), U256::MAX); + assert_eq!(or(U256::ZERO, U256::MAX), U256::MAX); + assert_eq!(or(U256::MAX, U256::MAX), U256::MAX); + assert_eq!(or(U256::ZERO, U256::ZERO), U256::ZERO); + } + + #[test] + fn test_xor() { + for i in 0..u8::MAX { + for j in 0..u8::MAX { + let a = U256::from(i); + let b = U256::from(j); + assert_eq!(xor(a, b), U256::from(i ^ j)) + } + } + + assert_eq!(xor(U256::MAX, U256::ZERO), U256::MAX); + assert_eq!(xor(U256::ZERO, U256::MAX), U256::MAX); + assert_eq!(xor(U256::MAX, U256::MAX), U256::ZERO); + assert_eq!(xor(U256::ZERO, U256::ZERO), U256::ZERO); + } + + #[test] + fn test_not() { + for i in 0..u8::MAX { + let mut expect = [0xff; 32]; + expect[31] = !i; + assert_eq!(not(U256::from(i)), U256::from(expect)) + } + + assert_eq!(not(U256::MAX), U256::ZERO); + assert_eq!(not(U256::ZERO), U256::MAX); + } +} diff --git a/actors/evm/src/interpreter/instructions/call.rs b/actors/evm/src/interpreter/instructions/call.rs new file mode 100644 index 000000000..1cfee4518 --- /dev/null +++ b/actors/evm/src/interpreter/instructions/call.rs @@ -0,0 +1,852 @@ +#![allow(clippy::too_many_arguments)] + +use fil_actors_evm_shared::{address::EthAddress, uints::U256}; +use fvm_ipld_encoding::ipld_block::IpldBlock; +use fvm_ipld_encoding::BytesDe; +use fvm_shared::{address::Address, sys::SendFlags, MethodNum, IPLD_RAW}; + +use crate::interpreter::{ + precompiles::{is_reserved_precompile_address, PrecompileContext}, + CallKind, +}; + +use super::ext::{get_contract_type, get_evm_bytecode_cid, ContractType}; + +use { + super::memory::{copy_to_memory, get_memory_region}, + crate::interpreter::instructions::memory::MemoryRegion, + crate::interpreter::precompiles, + crate::interpreter::ExecutionState, + crate::interpreter::System, + crate::{DelegateCallParams, Method}, + fil_actors_runtime::runtime::Runtime, + fil_actors_runtime::ActorError, + fvm_shared::econ::TokenAmount, + fvm_shared::error::ErrorNumber, +}; + +/// The gas granted on bare "transfers". +const TRANSFER_GAS_LIMIT: U256 = U256::from_u64(10_000_000); + +pub fn calldataload( + state: &mut ExecutionState, + _: &System, + index: U256, +) -> Result { + let input_len = state.input_data.len(); + Ok(index + .try_into() + .ok() + .filter(|&start| start < input_len) + .map(|start: usize| { + let end = core::cmp::min(start.saturating_add(crate::EVM_WORD_SIZE), input_len); + let mut data = [0; crate::EVM_WORD_SIZE]; + data[..end - start].copy_from_slice(&state.input_data[start..end]); + U256::from_big_endian(&data) + }) + .unwrap_or_default()) +} + +#[inline] +pub fn calldatasize( + state: &mut ExecutionState, + _: &System, +) -> Result { + Ok(u128::try_from(state.input_data.len()).unwrap().into()) +} + +#[inline] +pub fn calldatacopy( + state: &mut ExecutionState, + _: &System, + mem_index: U256, + input_index: U256, + size: U256, +) -> Result<(), ActorError> { + copy_to_memory(&mut state.memory, mem_index, size, input_index, &state.input_data, true) +} + +#[inline] +pub fn codesize( + _state: &mut ExecutionState, + _: &System, + code: &[u8], +) -> Result { + Ok(U256::from(code.len())) +} + +#[inline] +pub fn codecopy( + state: &mut ExecutionState, + _: &System, + code: &[u8], + mem_index: U256, + input_index: U256, + size: U256, +) -> Result<(), ActorError> { + copy_to_memory(&mut state.memory, mem_index, size, input_index, code, true) +} + +#[inline] +pub fn call_call( + state: &mut ExecutionState, + system: &mut System, + gas: U256, + dst: U256, + value: U256, + input_offset: U256, + input_size: U256, + output_offset: U256, + output_size: U256, +) -> Result { + call_generic( + state, + system, + CallKind::Call, + (gas, dst, value, input_offset, input_size, output_offset, output_size), + ) +} + +#[inline] +pub fn call_delegatecall( + state: &mut ExecutionState, + system: &mut System, + gas: U256, + dst: U256, + input_offset: U256, + input_size: U256, + output_offset: U256, + output_size: U256, +) -> Result { + call_generic( + state, + system, + CallKind::DelegateCall, + (gas, dst, U256::zero(), input_offset, input_size, output_offset, output_size), + ) +} + +#[inline] +pub fn call_staticcall( + state: &mut ExecutionState, + system: &mut System, + gas: U256, + dst: U256, + input_offset: U256, + input_size: U256, + output_offset: U256, + output_size: U256, +) -> Result { + call_generic( + state, + system, + CallKind::StaticCall, + (gas, dst, U256::zero(), input_offset, input_size, output_offset, output_size), + ) +} + +pub fn call_generic( + state: &mut ExecutionState, + system: &mut System, + kind: CallKind, + params: (U256, U256, U256, U256, U256, U256, U256), +) -> Result { + let ExecutionState { stack: _, memory, .. } = state; + + let (mut gas, dst, value, input_offset, input_size, output_offset, output_size) = params; + + if system.readonly && value > U256::zero() { + // non-zero sends are side-effects and hence a static mode violation + return Err(ActorError::read_only("cannot transfer value when read-only".into())); + } + + let input_region = get_memory_region(memory, input_offset, input_size)?; + + let (call_result, return_data) = { + // ref to memory is dropped after calling so we can mutate it on output later + let input_data = if let Some(MemoryRegion { offset, size }) = input_region { + &memory[offset..][..size.get()] + } else { + &[] + }; + + let dst: EthAddress = dst.into(); + if is_reserved_precompile_address(&dst) { + let context = PrecompileContext { + call_type: kind, + gas_limit: effective_gas_limit(system, gas), + value, + }; + + if log::log_enabled!(log::Level::Info) { + // log input to the precompile, but make sure we dont log _too_ much. + let mut input_hex = hex::encode(input_data); + input_hex.truncate(1024); + if input_data.len() > 512 { + input_hex.push_str("[..]") + } + log::info!(target: "evm", "Call Precompile:\n\taddress: {:x?}\n\tcontext: {:?}\n\tinput: {}", dst, context, input_hex); + } + + match precompiles::Precompiles::call_precompile(system, &dst, input_data, context) { + Ok(return_data) => (1, return_data), + Err(err) => { + log::warn!(target: "evm", "Precompile failed: error {:?}", err); + // precompile failed, exit with reverted and no output + (0, vec![]) + } + } + } else { + let call_result = match kind { + CallKind::Call | CallKind::StaticCall => { + let dst_addr: Address = dst.into(); + if (gas == 0 && value > 0) || (gas == 2300 && value == 0) { + // We provide enough gas for the transfer to succeed in all case. + gas = TRANSFER_GAS_LIMIT; + } + let gas_limit = Some(effective_gas_limit(system, gas)); + let params = if input_data.is_empty() { + None + } else { + Some(IpldBlock { codec: IPLD_RAW, data: input_data.into() }) + }; + let value = TokenAmount::from(&value); + let send_flags = if kind == CallKind::StaticCall { + SendFlags::READ_ONLY + } else { + SendFlags::default() + }; + // Error cases: + // + // 1. If the outer result fails, it means we failed to flush/restore state and + // there is a bug. We exit with an actor error and abort. + match system.send_raw( + &dst_addr, + Method::InvokeContract as MethodNum, + params, + value, + gas_limit, + send_flags, + )? { + Ok(resp) => { + if resp.exit_code.is_success() { + Ok(resp.return_data) + } else { + Err(resp.return_data) + } + } + Err(e) => match e { + // The target actor doesn't exist. To match EVM behavior, we walk away. + ErrorNumber::NotFound => Ok(None), + // If we hit this case, we must have tried to auto-deploy an actor + // while read-only. We've already checked that we aren't trying to + // transfer funds. + // + // To match EVM behavior, we treat this case as "success" and + // walk away. + ErrorNumber::ReadOnly + if system.readonly || kind == CallKind::StaticCall => + { + Ok(None) + } + ErrorNumber::InsufficientFunds => Err(None), + ErrorNumber::LimitExceeded => Err(None), + // Nothing else is expected in this case. This likely indicates a bug, but + // it doesn't indicate that there's an issue with _this_ EVM actor, so we + // might as log and well continue. + e => { + log::error!("unexpected syscall error on CALL to {dst_addr}: {e}"); + Err(None) + } + }, + } + } + CallKind::DelegateCall => match get_contract_type(system.rt, &dst) { + ContractType::EVM(dst_addr) => { + // If we're calling an actual EVM actor, get its code. + if let Some(code) = get_evm_bytecode_cid(system, &dst_addr)? { + // and then invoke self with delegate; readonly context is sticky + let params = DelegateCallParams { + code, + input: input_data.into(), + caller: state.caller, + value: state.value_received.clone(), + }; + system + .send( + &system.rt.message().receiver(), + Method::InvokeContractDelegate as u64, + IpldBlock::serialize_dag_cbor(¶ms)?, + TokenAmount::from(&value), + Some(effective_gas_limit(system, gas)), + SendFlags::default(), + ) + .map_err(|mut ae| ae.take_data()) + } else { + // If it doesn't have code, short-circuit and return immediately. + Ok(None) + } + } + // If we're calling an account or a non-existent actor, return nothing because + // this is how the EVM behaves. + ContractType::Account | ContractType::NotFound => Ok(None), + // If we're calling a "native" actor, always revert. + ContractType::Native(_) => { + log::info!("attempted to delegatecall a native actor at {dst:?}"); + Err(None) + } + ContractType::Precompile => { + log::error!("reached a precompile address in DelegateCall when a precompile should've been caught earlier in the system"); + Err(None) + } + }, + }; + let (code, data) = match call_result { + Ok(result) => (1, result), + Err(result) => (0, result), + }; + + ( + code, + match data { + // Support the "empty" result. We often use this to mean "returned nothing" and + // it's important to support, e.g., sending to accounts. + None => Vec::new(), + Some(r) => + // NOTE: If the user returns an invalid thing, we just the returned bytes as-is. + // We can't lie to the contract and say that the callee reverted, and we don't want + // to "abort". + { + r.deserialize().map(|BytesDe(d)| d).unwrap_or_else(|_| r.data) + } + }, + ) + } + }; + + state.return_data = return_data; + + // copy return data to output region if it is non-zero + copy_to_memory(memory, output_offset, output_size, U256::zero(), &state.return_data, false)?; + + Ok(U256::from(call_result)) +} + +fn effective_gas_limit(system: &System, gas: U256) -> u64 { + let gas_rsvp = (63 * system.rt.gas_available()) / 64; + let gas = gas.to_u64_saturating(); + std::cmp::min(gas, gas_rsvp) +} + +#[cfg(test)] +mod tests { + use crate::evm_unit_test; + use cid::Cid; + use fil_actors_evm_shared::uints::U256; + use fil_actors_runtime::test_utils::EVM_ACTOR_CODE_ID; + use fvm_ipld_blockstore::Blockstore; + use fvm_ipld_encoding::ipld_block::IpldBlock; + use fvm_ipld_encoding::IPLD_RAW; + use fvm_shared::address::Address as FilAddress; + use fvm_shared::error::{ErrorNumber, ExitCode}; + use fvm_shared::sys::SendFlags; + use num_traits::Zero; + + #[test] + fn test_calldataload() { + // happy path + evm_unit_test! { + (m) { + CALLDATALOAD; + } + m.state.input_data = vec![0x00, 0x01, 0x02]; + m.state.stack.push(U256::from(1)).unwrap(); + let result = m.step(); + assert!(result.is_ok(), "execution step failed"); + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), U256::from(0x0102) << 240); + }; + } + + #[test] + fn test_calldataload_oob() { + // tsests admissibility of out of bounds reads (as 0s) + evm_unit_test! { + (m) { + CALLDATALOAD; + } + m.state.input_data = vec![0x00, 0x01, 0x02]; + m.state.stack.push(U256::from(10)).unwrap(); + let result = m.step(); + assert!(result.is_ok(), "execution step failed"); + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), U256::zero()); + }; + } + + #[test] + fn test_calldataload_large_input() { + // tests admissibility of subset of data reads + evm_unit_test! { + (m) { + CALLDATALOAD; + } + let mut input_data = [0u8;64]; + input_data[0] = 0x42; + m.state.input_data = Vec::from(input_data); + m.state.stack.push(U256::from(0)).unwrap(); + let result = m.step(); + assert!(result.is_ok(), "execution step failed"); + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), U256::from(0x42) << 248); + }; + } + + #[test] + fn test_calldatasize() { + evm_unit_test! { + (m) { + CALLDATASIZE; + } + m.state.input_data = vec![0x00, 0x01, 0x02]; + let result = m.step(); + assert!(result.is_ok(), "execution step failed"); + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), U256::from(3)); + }; + } + + #[test] + fn test_calldatacopy() { + // happy path + evm_unit_test! { + (m) { + CALLDATACOPY; + } + m.state.input_data = vec![0x00, 0x01, 0x02]; + m.state.stack.push(U256::from(2)).unwrap(); // length + m.state.stack.push(U256::from(1)).unwrap(); // offset + m.state.stack.push(U256::from(0)).unwrap(); // dest-offset + let result = m.step(); + assert!(result.is_ok(), "execution step failed"); + assert_eq!(m.state.stack.len(), 0); + let mut expected = [0u8; 32]; + expected[0] = 0x01; + expected[1] = 0x02; + assert_eq!(&*m.state.memory, &expected); + }; + } + + #[test] + fn test_calldatacopy_oob() { + // tests admissibility of large (oob) lengths; should give zeros. + evm_unit_test! { + (m) { + CALLDATACOPY; + } + m.state.input_data = vec![0x00, 0x01, 0x02]; + m.state.stack.push(U256::from(64)).unwrap(); // length -- too big + m.state.stack.push(U256::from(1)).unwrap(); // offset + m.state.stack.push(U256::from(0)).unwrap(); // dest-offset + let result = m.step(); + assert!(result.is_ok(), "execution step failed"); + assert_eq!(m.state.stack.len(), 0); + let mut expected = [0u8; 64]; + expected[0] = 0x01; + expected[1] = 0x02; + assert_eq!(&*m.state.memory, &expected); + }; + } + + #[test] + fn test_calldatacopy_oob2() { + // test admissibility of large (oob) offsetes -- should give zeros. + evm_unit_test! { + (m) { + CALLDATACOPY; + } + m.state.input_data = vec![0x00, 0x01, 0x02]; + m.state.stack.push(U256::from(2)).unwrap(); // length + m.state.stack.push(U256::from(10)).unwrap(); // offset -- out of bounds + m.state.stack.push(U256::from(0)).unwrap(); // dest-offset + let result = m.step(); + assert!(result.is_ok(), "execution step failed"); + assert_eq!(m.state.stack.len(), 0); + let expected = [0u8; 32]; + assert_eq!(&*m.state.memory, &expected); + }; + } + + #[test] + fn test_codesize() { + evm_unit_test! { + (m) { + CODESIZE; + JUMPDEST; + JUMPDEST; + JUMPDEST; + } + let result = m.step(); + assert!(result.is_ok(), "execution step failed"); + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), U256::from(4)); + }; + } + + #[test] + fn test_codecopy() { + evm_unit_test! { + (m) { + CODECOPY; + JUMPDEST; + JUMPDEST; + JUMPDEST; + } + m.state.stack.push(U256::from(4)).unwrap(); + m.state.stack.push(U256::from(0)).unwrap(); + m.state.stack.push(U256::from(0)).unwrap(); + let result = m.step(); + assert!(result.is_ok(), "execution step failed"); + assert_eq!(m.state.stack.len(), 0); + assert_eq!(m.state.memory[0..4], m.bytecode[..]); + }; + } + + #[test] + fn test_codecopy_partial() { + evm_unit_test! { + (m) { + CODECOPY; + JUMPDEST; + JUMPDEST; + JUMPDEST; + } + m.state.stack.push(U256::from(3)).unwrap(); + m.state.stack.push(U256::from(1)).unwrap(); + m.state.stack.push(U256::from(0)).unwrap(); + let result = m.step(); + assert!(result.is_ok(), "execution step failed"); + assert_eq!(m.state.stack.len(), 0); + assert_eq!(m.state.memory[0..3], m.bytecode[1..4]); + }; + } + + #[test] + fn test_codecopy_oob() { + evm_unit_test! { + (m) { + CODECOPY; + JUMPDEST; + JUMPDEST; + JUMPDEST; + } + m.state.stack.push(U256::from(4)).unwrap(); + m.state.stack.push(U256::from(1)).unwrap(); + m.state.stack.push(U256::from(0)).unwrap(); + let result = m.step(); + assert!(result.is_ok(), "execution step failed"); + assert_eq!(m.state.stack.len(), 0); + assert_eq!(m.state.memory[0..3], m.bytecode[1..4]); + }; + } + + #[test] + fn test_call() { + let dest = EthAddress::from_id(1001); + let fil_dest = FilAddress::new_id(1001); + let input_data = vec![0x01, 0x02, 0x03, 0x04]; + let output_data = vec![0xCA, 0xFE, 0xBA, 0xBE]; + evm_unit_test! { + (rt) { + rt.in_call = true; + rt.expect_send( + fil_dest, + crate::Method::InvokeContract as u64, + Some(IpldBlock { codec: IPLD_RAW, data: input_data }), + TokenAmount::zero(), + Some(1_000_000_000), + SendFlags::empty(), + Some(IpldBlock { codec: IPLD_RAW, data: output_data.clone() }), + ExitCode::OK, + None, + ); + rt.expect_gas_available(10_000_000_000); + } + (m) { + // input data + PUSH4; 0x01; 0x02; 0x03; 0x04; + PUSH0; + MSTORE; + // the call + CALL; + } + m.state.stack.push(U256::from(4)).unwrap(); // output size + m.state.stack.push(U256::from(0)).unwrap(); // output offset + m.state.stack.push(U256::from(4)).unwrap(); // input size + m.state.stack.push(U256::from(28)).unwrap(); // input offset + m.state.stack.push(U256::from(0)).unwrap(); // value + m.state.stack.push(dest.as_evm_word()).unwrap(); // dest + m.state.stack.push(U256::from(1_000_000_000)).unwrap(); // gas + for _ in 0..4 { + m.step().expect("execution step failed"); + } + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), U256::from(1)); + assert_eq!(&m.state.return_data, &output_data); + assert_eq!(&m.state.memory[0..4], &output_data); + }; + } + + #[test] + fn test_call_revert() { + let dest = EthAddress::from_id(1001); + let fil_dest = FilAddress::new_id(1001); + let input_data = vec![0x01, 0x02, 0x03, 0x04]; + let output_data = vec![0xCA, 0xFE, 0xBA, 0xBE]; + evm_unit_test! { + (rt) { + rt.in_call = true; + rt.expect_send( + fil_dest, + crate::Method::InvokeContract as u64, + Some(IpldBlock { codec: IPLD_RAW, data: input_data }), + TokenAmount::zero(), + Some(1_000_000_000), + SendFlags::empty(), + Some(IpldBlock { codec: IPLD_RAW, data: output_data.clone() }), + crate::EVM_CONTRACT_REVERTED, + None, + ); + rt.expect_gas_available(10_000_000_000); + } + (m) { + // input data + PUSH4; 0x01; 0x02; 0x03; 0x04; + PUSH0; + MSTORE; + // the call + CALL; + } + m.state.stack.push(U256::from(4)).unwrap(); // output size + m.state.stack.push(U256::from(0)).unwrap(); // output offset + m.state.stack.push(U256::from(4)).unwrap(); // input size + m.state.stack.push(U256::from(28)).unwrap(); // input offset + m.state.stack.push(U256::from(0)).unwrap(); // value + m.state.stack.push(dest.as_evm_word()).unwrap(); // dest + m.state.stack.push(U256::from(1_000_000_000)).unwrap(); // gas + for _ in 0..4 { + m.step().expect("execution step failed"); + } + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), U256::from(0)); + assert_eq!(&m.state.return_data, &output_data); + assert_eq!(&m.state.memory[0..4], &output_data); + }; + } + + #[test] + fn test_call_err() { + let dest = EthAddress::from_id(1001); + let fil_dest = FilAddress::new_id(1001); + let input_data = vec![0x01, 0x02, 0x03, 0x04]; + evm_unit_test! { + (rt) { + rt.in_call = true; + rt.expect_send( + fil_dest, + crate::Method::InvokeContract as u64, + Some(IpldBlock { codec: IPLD_RAW, data: input_data }), + TokenAmount::zero(), + Some(1_000_000_000), + SendFlags::empty(), + None, + ExitCode::OK, + Some(ErrorNumber::IllegalOperation), + ); + rt.expect_gas_available(10_000_000_000); + } + (m) { + // input data + PUSH4; 0x01; 0x02; 0x03; 0x04; + PUSH0; + MSTORE; + // the call + CALL; + } + m.state.stack.push(U256::from(4)).unwrap(); // output size + m.state.stack.push(U256::from(0)).unwrap(); // output offset + m.state.stack.push(U256::from(4)).unwrap(); // input size + m.state.stack.push(U256::from(28)).unwrap(); // input offset + m.state.stack.push(U256::from(0)).unwrap(); // value + m.state.stack.push(dest.as_evm_word()).unwrap(); // dest + m.state.stack.push(U256::from(1_000_000_000)).unwrap(); // gas + for _ in 0..4 { + m.step().expect("execution step failed"); + } + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), U256::from(0)); + assert_eq!(m.state.return_data.len(), 0); + }; + } + + #[test] + fn test_call_precompile() { + let mut id_bytes = [0u8; 20]; + id_bytes[19] = 0x04; + let dest = EthAddress(id_bytes); + let mut output_data = [0u8; 32]; + output_data[28] = 0x01; + output_data[29] = 0x02; + output_data[30] = 0x03; + output_data[31] = 0x04; + evm_unit_test! { + (rt) { + rt.in_call = true; + rt.expect_gas_available(10_000_000_000); + } + (m) { + // input data + PUSH4; 0x01; 0x02; 0x03; 0x04; + PUSH0; + MSTORE; + // the call + CALL; + } + m.state.stack.push(U256::from(32)).unwrap(); // output size + m.state.stack.push(U256::from(0)).unwrap(); // output offset + m.state.stack.push(U256::from(32)).unwrap(); // input size + m.state.stack.push(U256::from(0)).unwrap(); // input offset + m.state.stack.push(U256::from(0)).unwrap(); // value + m.state.stack.push(dest.as_evm_word()).unwrap(); // dest + m.state.stack.push(U256::from(1_000_000_000)).unwrap(); // gas + for _ in 0..4 { + m.step().expect("execution step failed"); + } + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), U256::from(1)); + assert_eq!(&m.state.return_data, &output_data); + assert_eq!(&m.state.memory[..], &output_data); + }; + } + + #[test] + fn test_delegatecall() { + let receiver = FilAddress::new_id(1000); + let dest = EthAddress::from_id(1001); + let caller = EthAddress::from_id(1002); + let fil_dest = FilAddress::new_id(1001); + let input_data = vec![0x01, 0x02, 0x03, 0x04]; + let output_data = vec![0xCA, 0xFE, 0xBA, 0xBE]; + evm_unit_test! { + (rt) { + rt.in_call = true; + rt.receiver = receiver; + rt.set_address_actor_type(fil_dest, *EVM_ACTOR_CODE_ID); + + let bytecode = vec![0xFE, 0xED, 0x43, 0x33]; + let bytecode_cid = Cid::try_from("baeaikaia").unwrap(); + rt.store.put_keyed(&bytecode_cid, bytecode.as_slice()).unwrap(); + + rt.expect_send( + fil_dest, + crate::Method::GetBytecode as u64, + Default::default(), + TokenAmount::zero(), + None, + SendFlags::READ_ONLY, + IpldBlock::serialize_cbor(&bytecode_cid).unwrap(), + ExitCode::OK, + None, + ); + + let params = crate::DelegateCallParams { + code: bytecode_cid, + input: input_data, + caller, + value: TokenAmount::zero(), + }; + + rt.expect_send( + receiver, + crate::Method::InvokeContractDelegate as u64, + IpldBlock::serialize_dag_cbor(¶ms).unwrap(), + TokenAmount::zero(), + Some(1_000_000_000), + SendFlags::empty(), + Some(IpldBlock { codec: IPLD_RAW, data: output_data.clone() }), + ExitCode::OK, + None, + ); + rt.expect_gas_available(10_000_000_000); + } + (m) { + // input data + PUSH4; 0x01; 0x02; 0x03; 0x04; + PUSH0; + MSTORE; + // the call + DELEGATECALL; + } + m.state.caller = caller; + m.state.stack.push(U256::from(4)).unwrap(); // output size + m.state.stack.push(U256::from(0)).unwrap(); // output offset + m.state.stack.push(U256::from(4)).unwrap(); // input size + m.state.stack.push(U256::from(28)).unwrap(); // input offset + m.state.stack.push(dest.as_evm_word()).unwrap(); // dest + m.state.stack.push(U256::from(1_000_000_000)).unwrap(); // gas + for _ in 0..4 { + m.step().expect("execution step failed"); + } + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), U256::from(1)); + assert_eq!(&m.state.return_data, &output_data); + assert_eq!(&m.state.memory[0..4], &output_data); + }; + } + + #[test] + fn test_staticcall() { + let dest = EthAddress::from_id(1001); + let fil_dest = FilAddress::new_id(1001); + let input_data = vec![0x01, 0x02, 0x03, 0x04]; + let output_data = vec![0xCA, 0xFE, 0xBA, 0xBE]; + evm_unit_test! { + (rt) { + rt.in_call = true; + rt.expect_send( + fil_dest, + crate::Method::InvokeContract as u64, + Some(IpldBlock { codec: IPLD_RAW, data: input_data }), + TokenAmount::zero(), + Some(1_000_000_000), + SendFlags::READ_ONLY, + Some(IpldBlock { codec: IPLD_RAW, data: output_data.clone() }), + ExitCode::OK, + None, + ); + rt.expect_gas_available(10_000_000_000); + } + (m) { + // input data + PUSH4; 0x01; 0x02; 0x03; 0x04; + PUSH0; + MSTORE; + // the call + STATICCALL; + } + m.state.stack.push(U256::from(4)).unwrap(); // output size + m.state.stack.push(U256::from(0)).unwrap(); // output offset + m.state.stack.push(U256::from(4)).unwrap(); // input size + m.state.stack.push(U256::from(28)).unwrap(); // input offset + m.state.stack.push(dest.as_evm_word()).unwrap(); // dest + m.state.stack.push(U256::from(1_000_000_000)).unwrap(); // gas + for _ in 0..4 { + m.step().expect("execution step failed"); + } + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), U256::from(1)); + assert_eq!(&m.state.return_data, &output_data); + assert_eq!(&m.state.memory[0..4], &output_data); + }; + } +} diff --git a/actors/evm/src/interpreter/instructions/context.rs b/actors/evm/src/interpreter/instructions/context.rs new file mode 100644 index 000000000..8001e502d --- /dev/null +++ b/actors/evm/src/interpreter/instructions/context.rs @@ -0,0 +1,436 @@ +use fil_actors_evm_shared::uints::U256; +use fil_actors_runtime::ActorError; +use fvm_shared::clock::ChainEpoch; + +use crate::EVM_WORD_SIZE; + +use { + crate::interpreter::{ExecutionState, System}, + fil_actors_runtime::runtime::Runtime, +}; + +#[inline] +pub fn blockhash( + _state: &mut ExecutionState, + system: &System, + bn: U256, +) -> Result { + let result = bn + .try_into() + .ok() + .filter(|&height: &ChainEpoch| { + // The EVM allows fetching blockhashes from the 256 _previous_ blocks, not including the + // current. The FVM allows fetching block CIDs from the last 899 epochs, not including + // the current epoch. + let curr_epoch = system.rt.curr_epoch(); + height >= curr_epoch - 256 && height < curr_epoch + }) + .and_then(|height| system.rt.tipset_cid(height).ok()) + .map(|cid| { + let mut hash = cid.hash().digest(); + if hash.len() > EVM_WORD_SIZE { + hash = &hash[..EVM_WORD_SIZE] + } + U256::from_big_endian(hash) + }) + .unwrap_or_default(); + Ok(result) +} + +#[inline] +pub fn caller(state: &mut ExecutionState, _: &System) -> Result { + Ok(state.caller.as_evm_word()) +} + +#[inline] +pub fn address( + state: &mut ExecutionState, + _system: &System, +) -> Result { + Ok(state.receiver.as_evm_word()) +} + +#[inline] +pub fn origin( + _state: &mut ExecutionState, + system: &System, +) -> Result { + let origin_addr = system + .resolve_ethereum_address(&system.rt.message().origin()) + .expect("failed to resolve origin address"); + Ok(origin_addr.as_evm_word()) +} + +#[inline] +pub fn call_value( + state: &mut ExecutionState, + _system: &System, +) -> Result { + Ok(U256::from(&state.value_received)) +} + +#[inline] +pub fn coinbase( + _state: &mut ExecutionState, + _system: &System, +) -> Result { + // Eth zero address, beneficiary of the current block doesn't make much sense in Filecoin due to multiple winners in each block. + Ok(U256::zero()) +} + +#[inline] +pub fn gas_price( + _state: &mut ExecutionState, + system: &System, +) -> Result { + let effective_price = system.rt.base_fee() + system.rt.message().gas_premium(); + Ok(U256::from(&effective_price)) +} + +#[inline] +pub fn gas(_state: &mut ExecutionState, system: &System) -> Result { + Ok(U256::from(system.rt.gas_available())) +} + +#[inline] +pub fn timestamp( + _state: &mut ExecutionState, + system: &System, +) -> Result { + Ok(U256::from(system.rt.tipset_timestamp())) +} + +#[inline] +pub fn block_number( + _state: &mut ExecutionState, + system: &System, +) -> Result { + // NOTE: Panics if current epoch is negative, which should never happen in the network + Ok(U256::from(system.rt.curr_epoch())) +} + +/// EIP-4399: DIFFICULTY -> PREVRANDAO +#[inline] +pub fn prevrandao( + _state: &mut ExecutionState, + system: &mut System, +) -> Result { + // NOTE: Filecoin beacon randomness is expected to fall outside of the `2^64` reserved range, following PREVRANDAO's assumptions. + // NOTE: EVM uses previous RANDAO value in this opcode since the _current_ RANDAO for them runs on the beacon chain's state + // and wont be finalized till the end of a block. Filecoin's chain randomness is generated _before_ any contract is run, so we instead + // grab randomness from the current epoch. + system.get_randomness().map(|v| U256::from(*v)) +} + +#[inline] +pub fn gas_limit( + _state: &mut ExecutionState, + _system: &System, +) -> Result { + const BLOCK_GAS_LIMIT: u64 = 10_000_000_000u64; + Ok(U256::from(BLOCK_GAS_LIMIT)) +} + +#[inline] +pub fn chain_id( + _state: &mut ExecutionState, + system: &System, +) -> Result { + Ok(U256::from_u64(system.rt.chain_id().into())) +} + +#[inline] +pub fn base_fee( + _state: &mut ExecutionState, + system: &System, +) -> Result { + Ok(U256::from(&system.rt.base_fee())) +} + +#[cfg(test)] +mod tests { + use crate::evm_unit_test; + use cid::Cid; + use fil_actors_evm_shared::uints::U256; + use fil_actors_runtime::EAM_ACTOR_ID; + use fvm_ipld_encoding::{DAG_CBOR, IPLD_RAW}; + use fvm_shared::address::Address as FilAddress; + + #[test] + fn test_blockhash() { + // truncate to 32 bytes + let counting_byte_hash: Vec = (0..40u8).collect(); + let long_unknown = + Cid::new_v1(IPLD_RAW, multihash::Multihash::wrap(0, &counting_byte_hash).unwrap()); + let long_expect = counting_byte_hash[..32].try_into().unwrap(); + // multihash code ignored + let cbor_odd_hash = + Cid::new_v1(DAG_CBOR, multihash::Multihash::wrap(123, &[0xfe; 32]).unwrap()); + let cbor_odd_expect = [0xfe; 32]; + + let nothing = [0; 32]; + + for (current, getting, insert, expect, test) in [ + ( + 12345, + 12340u16, + Some(long_unknown), + long_expect, + "truncated tipset hash, (first 32 bytes)", + ), + (1234, 1230u16, Some(cbor_odd_hash), cbor_odd_expect, "normal-ish tipset"), + (123, 222u16, None, nothing, "future tipset"), + (1234, 123u16, None, nothing, "requested older than finality (256)"), + ] { + let [a, b] = getting.to_be_bytes(); + evm_unit_test! { + (rt) { + rt.in_call = true; + rt.set_epoch(current); + rt.tipset_cids.resize(current as usize, Cid::default()); + if let Some(cid) = insert { + rt.tipset_cids[getting as usize] = cid; + } + } + (m) { + PUSH2; + {a}; + {b}; + BLOCKHASH; + } + m.step().expect("execution step failed"); + m.step().expect("execution step failed"); + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), U256::from(expect), "{}", test); + }; + } + } + + #[test] + fn test_callvalue() { + evm_unit_test! { + (m) { + CALLVALUE; + } + m.state.value_received = TokenAmount::from_atto(123); + let result = m.step(); + assert!(result.is_ok(), "execution step failed"); + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), U256::from(123)); + }; + } + + #[test] + fn test_number() { + for epoch in [1234, i64::MAX, 0, 1] { + evm_unit_test! { + (rt) { + rt.set_epoch(epoch); + } + (m) { + NUMBER; + } + m.step().expect("execution step failed"); + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), U256::from(epoch)); + }; + } + } + + #[test] + fn test_chainid() { + for chainid in [31415, 3141, 0, 1] { + evm_unit_test! { + (rt) { + rt.chain_id = chainid.into(); + } + (m) { + CHAINID; + } + m.step().expect("execution step failed"); + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), U256::from(chainid)); + }; + } + } + + #[test] + fn test_basefee() { + for basefee in [12345, u128::MAX, 0, 1].map(U256::from) { + evm_unit_test! { + (rt) { + rt.base_fee = TokenAmount::from(&basefee); + } + (m) { + BASEFEE; + } + m.step().expect("execution step failed"); + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), basefee); + }; + } + } + + #[test] + fn test_coinbase() { + evm_unit_test! { + (m) { + COINBASE; + } + m.step().expect("execution step failed"); + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), U256::ZERO); + }; + } + + #[test] + fn test_timestamp() { + evm_unit_test! { + (rt) { + rt.tipset_timestamp = 12345; + } + (m) { + TIMESTAMP; + } + m.step().expect("execution step failed"); + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), U256::from(12345)); + }; + } + + #[test] + fn test_prevrandao() { + let epoch = 1234; + evm_unit_test! { + (rt) { + rt.set_epoch(epoch); + rt.expect_get_randomness_from_beacon(fil_actors_runtime::runtime::DomainSeparationTag::EvmPrevRandao, epoch, Vec::from(*b"prevrandao"), [0xff; 32]); + } + (m) { + PREVRANDAO; + } + m.step().expect("execution step failed"); + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), U256::MAX); + }; + } + + #[test] + fn test_gas_limit() { + for limit in [12345, 0, u64::MAX] { + evm_unit_test! { + (rt) { + rt.gas_limit = limit; + } + (m) { + GASLIMIT; + } + m.step().expect("execution step failed"); + assert_eq!(m.state.stack.len(), 1); + // always block gas limit + assert_eq!(m.state.stack.pop().unwrap(), U256::from(10_000_000_000u64)); + }; + } + } + + #[test] + fn test_address() { + evm_unit_test! { + (m) { + ADDRESS; + } + let addr = EthAddress::from_id(1001); + m.state.receiver = addr; + m.step().expect("execution step failed"); + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), addr.as_evm_word()); + }; + } + + #[test] + fn test_origin_id() { + let eth_addr = EthAddress::from_id(1000); // default origin in construction of rt in macro + let fil_addr = FilAddress::new_id(1000); + evm_unit_test! { + (rt) { + rt.in_call = true; + rt.set_origin(fil_addr); + } + (m) { + ORIGIN; + } + m.step().expect("execution step failed"); + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), eth_addr.as_evm_word()); + }; + } + + #[test] + fn test_origin_ethaddr() { + let addr_bytes = hex_literal::hex!("FEEDFACECAFEBEEF000000000000000000001234"); + let eth_addr = EthAddress(addr_bytes); + let fil_addr = FilAddress::new_delegated(EAM_ACTOR_ID, &addr_bytes).unwrap(); + evm_unit_test! { + (rt) { + rt.in_call = true; + rt.set_origin(fil_addr); + } + (m) { + ORIGIN; + } + m.step().expect("execution step failed"); + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), eth_addr.as_evm_word()); + }; + } + + #[test] + fn test_caller() { + evm_unit_test! { + (m) { + CALLER; + } + let addr = EthAddress::from_id(1001); + m.state.caller = addr; + m.step().expect("execution step failed"); + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), addr.as_evm_word()); + }; + } + + #[test] + fn test_gasprice() { + // Note: This is currently buggy, as the price needs to be capped by the MaxFeeCap. + evm_unit_test! { + (rt) { + rt.base_fee = TokenAmount::from_atto(1000); + rt.gas_premium = TokenAmount::from_atto(1234); + } + (m) { + GASPRICE; + } + let addr = EthAddress::from_id(1001); + m.state.caller = addr; + m.step().expect("execution step failed"); + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), U256::from(2234)); + }; + } + + #[test] + fn test_gas() { + evm_unit_test! { + (rt) { + rt.expect_gas_available(1234000); + } + (m) { + GAS; + } + let addr = EthAddress::from_id(1001); + m.state.caller = addr; + m.step().expect("execution step failed"); + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), U256::from(1234000)); + }; + } +} diff --git a/actors/evm/src/interpreter/instructions/control.rs b/actors/evm/src/interpreter/instructions/control.rs new file mode 100644 index 000000000..334d64838 --- /dev/null +++ b/actors/evm/src/interpreter/instructions/control.rs @@ -0,0 +1,455 @@ +use fil_actors_evm_shared::uints::U256; +use fil_actors_runtime::{ActorError, AsActorError}; + +use crate::{ + interpreter::{memory::Memory, output::Outcome, Output}, + EVM_CONTRACT_BAD_JUMPDEST, EVM_CONTRACT_ILLEGAL_MEMORY_ACCESS, + EVM_CONTRACT_INVALID_INSTRUCTION, +}; + +use { + super::memory::get_memory_region, + crate::interpreter::Bytecode, + crate::interpreter::{ExecutionState, System}, + fil_actors_runtime::runtime::Runtime, +}; + +#[inline] +pub fn nop(_state: &mut ExecutionState, _system: &System) -> Result<(), ActorError> { + Ok(()) +} + +#[inline] +pub fn invalid( + _state: &mut ExecutionState, + _system: &System, +) -> Result<(), ActorError> { + Err(ActorError::unchecked(EVM_CONTRACT_INVALID_INSTRUCTION, "invalid instruction".into())) +} + +#[inline] +pub fn ret( + state: &mut ExecutionState, + _system: &System, + offset: U256, + size: U256, +) -> Result { + exit(&mut state.memory, offset, size, Outcome::Return) +} + +#[inline] +pub fn revert( + state: &mut ExecutionState, + _system: &System, + offset: U256, + size: U256, +) -> Result { + exit(&mut state.memory, offset, size, Outcome::Revert) +} + +#[inline] +pub fn stop( + _state: &mut ExecutionState, + _system: &System, +) -> Result { + Ok(Output { return_data: Vec::new(), outcome: Outcome::Return }) +} + +#[inline] +fn exit( + memory: &mut Memory, + offset: U256, + size: U256, + status: Outcome, +) -> Result { + Ok(Output { + outcome: status, + return_data: super::memory::get_memory_region(memory, offset, size)? + .map(|region| memory[region.offset..region.offset + region.size.get()].to_vec()) + .unwrap_or_default(), + }) +} + +#[inline] +pub fn returndatasize( + state: &mut ExecutionState, + _system: &System, +) -> Result { + Ok(U256::from(state.return_data.len())) +} + +#[inline] +pub fn returndatacopy( + state: &mut ExecutionState, + _system: &System, + mem_index: U256, + input_index: U256, + size: U256, +) -> Result<(), ActorError> { + let region = get_memory_region(&mut state.memory, mem_index, size)?; + + let src: usize = input_index + .try_into() + .context_code(EVM_CONTRACT_ILLEGAL_MEMORY_ACCESS, "returndatacopy index exceeds max u32")?; + if src > state.return_data.len() { + return Err(ActorError::unchecked( + EVM_CONTRACT_ILLEGAL_MEMORY_ACCESS, + format!( + "returndatacopy start {} exceeds return-data length {}", + src, + state.return_data.len() + ), + )); + } + + let end = src + .checked_add(region.as_ref().map(|r| r.size.get()).unwrap_or(0)) + .context_code(EVM_CONTRACT_ILLEGAL_MEMORY_ACCESS, "returndatacopy end exceeds max u32")?; + + if end > state.return_data.len() { + return Err(ActorError::unchecked( + EVM_CONTRACT_ILLEGAL_MEMORY_ACCESS, + format!( + "returndatacopy end {} exceeds return-data length {}", + src, + state.return_data.len() + ), + )); + } + + if let Some(region) = region { + state.memory[region.offset..region.offset + region.size.get()] + .copy_from_slice(&state.return_data[src..src + region.size.get()]); + } + + Ok(()) +} + +#[inline] +pub fn jump(bytecode: &Bytecode, _pc: usize, dest: U256) -> Result { + let dst = dest.try_into().context_code(EVM_CONTRACT_BAD_JUMPDEST, "jumpdest exceeds u32")?; + if !bytecode.valid_jump_destination(dst) { + return Err(ActorError::unchecked( + EVM_CONTRACT_BAD_JUMPDEST, + format!("jumpdest {dst} is invalid"), + )); + } + // skip the JMPDEST noop sled + Ok(dst + 1) +} + +#[inline] +pub fn jumpi(bytecode: &Bytecode, pc: usize, dest: U256, test: U256) -> Result { + if !test.is_zero() { + let dst = + dest.try_into().context_code(EVM_CONTRACT_BAD_JUMPDEST, "jumpdest exceeds u32")?; + if !bytecode.valid_jump_destination(dst) { + return Err(ActorError::unchecked( + EVM_CONTRACT_BAD_JUMPDEST, + format!("jumpdest {dst} is invalid"), + )); + } + // skip the JMPDEST noop sled + Ok(dst + 1) + } else { + Ok(pc + 1) + } +} + +#[cfg(test)] +mod tests { + use fil_actors_evm_shared::uints::U256; + + use crate::evm_unit_test; + use crate::{EVM_CONTRACT_BAD_JUMPDEST, EVM_CONTRACT_ILLEGAL_MEMORY_ACCESS}; + + #[test] + fn test_jump() { + evm_unit_test! { + (m) { + JUMP; + JUMPDEST; + JUMPDEST; + JUMPDEST; + JUMPDEST; + } + m.state.stack.push(U256::from(2)).unwrap(); + let result = m.step(); + assert!(result.is_ok(), "execution step failed"); + assert_eq!(m.state.stack.len(), 0); + assert_eq!(m.pc, 3, "pc has not advanced to 3"); + }; + } + + #[test] + fn test_jump_err() { + evm_unit_test!( + (m) { + JUMP; // JUMP + PUSH4; // PUSH4 -- garbage + 0x01; // garbage + 0x02; // garbage + 0x03; // garbage + 0x04; // garbage + } + m.state.stack.push(U256::from(2)).unwrap(); + let result = m.step(); + assert_eq!(m.state.stack.len(), 0); + assert!(result.is_err(), "execution step succeeded"); + assert_eq!(result.err().unwrap().exit_code(), EVM_CONTRACT_BAD_JUMPDEST); + ); + } + + #[test] + fn test_jump_err2() { + evm_unit_test! { + (m) { + JUMP; // JUMP + PUSH4; // PUSH4 -- garbage + 0x01; // garbage + 0x02; // garbage + 0x03; // garbage + 0x04; // garbage + } + + m.state.stack.push(U256::from(123)).unwrap(); + let result = m.step(); + assert_eq!(m.state.stack.len(), 0); + assert!(result.is_err(), "execution step succeeded"); + assert_eq!(result.err().unwrap().exit_code(), EVM_CONTRACT_BAD_JUMPDEST); + }; + } + + #[test] + fn test_jump_err3() { + evm_unit_test! { + (m) { + JUMP; + PUSH4; + JUMPDEST; + JUMPDEST; + JUMPDEST; + JUMPDEST; + } + m.state.stack.push(U256::from(2)).unwrap(); + let result = m.step(); + assert_eq!(m.state.stack.len(), 0); + assert!(result.is_err(), "execution step succeeded"); + assert_eq!(result.err().unwrap().exit_code(), EVM_CONTRACT_BAD_JUMPDEST); + }; + } + + #[test] + fn test_jumpi_t() { + evm_unit_test! { + (m) { + JUMPI; + JUMPDEST; + JUMPDEST; + JUMPDEST; + JUMPDEST; + } + m.state.stack.push(U256::from(1)).unwrap(); + m.state.stack.push(U256::from(2)).unwrap(); + let result = m.step(); + assert!(result.is_ok(), "execution step failed"); + assert_eq!(m.state.stack.len(), 0); + assert_eq!(m.pc, 3, "pc has not advanced to 3"); + } + } + + #[test] + fn test_jumpi_f() { + evm_unit_test! { + (m) { + JUMPI; + JUMPDEST; + JUMPDEST; + JUMPDEST; + JUMPDEST; + } + m.state.stack.push(U256::from(0)).unwrap(); + m.state.stack.push(U256::from(2)).unwrap(); + let result = m.step(); + assert!(result.is_ok(), "execution step failed"); + assert_eq!(m.state.stack.len(), 0); + assert_eq!(m.pc, 1, "pc has not advanced to 1"); + }; + } + + #[test] + fn test_jumpi_err() { + evm_unit_test! { + (m) { + JUMPI; + JUMPDEST; + JUMPDEST; + JUMPDEST; + JUMPDEST; + } + m.state.stack.push(U256::from(1)).unwrap(); + m.state.stack.push(U256::from(123)).unwrap(); + let result = m.step(); + assert_eq!(m.state.stack.len(), 0); + assert!(result.is_err(), "execution step succeeded"); + assert_eq!(result.err().unwrap().exit_code(), EVM_CONTRACT_BAD_JUMPDEST); + }; + } + + #[test] + fn test_pc() { + evm_unit_test! { + (m) { + PC; + JUMPDEST; + } + let result = m.step(); + assert!(result.is_ok(), "execution step failed"); + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), U256::zero()); + assert_eq!(m.pc, 1, "pc has not advanced to 1"); + } + } + + #[test] + fn test_returndatasize() { + evm_unit_test! { + (m) { + RETURNDATASIZE; + } + m.state.return_data = vec![0x00, 0x01, 0x02]; + let result = m.step(); + assert!(result.is_ok(), "execution step failed"); + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), U256::from(3)); + }; + } + + #[test] + fn test_returndatacopy() { + // happy path + evm_unit_test! { + (m) { + RETURNDATACOPY; + } + m.state.return_data = vec![0x00, 0x01, 0x02]; + m.state.stack.push(U256::from(2)).unwrap(); // length + m.state.stack.push(U256::from(1)).unwrap(); // offset + m.state.stack.push(U256::from(0)).unwrap(); // dest-offset + let result = m.step(); + assert!(result.is_ok(), "execution step failed"); + assert_eq!(m.state.stack.len(), 0); + let mut expected = [0u8; 32]; + expected[0] = 0x01; + expected[1] = 0x02; + assert_eq!(&*m.state.memory, &expected); + }; + } + + #[test] + fn test_returndatacopy_err() { + // error condition 1: length too large + evm_unit_test! { + (m) { + RETURNDATACOPY; + } + m.state.return_data = vec![0x00, 0x01, 0x02]; + m.state.stack.push(U256::from(10)).unwrap(); // length + m.state.stack.push(U256::from(1)).unwrap(); // offset + m.state.stack.push(U256::from(0)).unwrap(); // dest-offset + let result = m.step(); + assert!(result.is_err(), "execution step succeeded"); + assert_eq!(result.err().unwrap().exit_code(), EVM_CONTRACT_ILLEGAL_MEMORY_ACCESS); + }; + } + + #[test] + fn test_returndatacopy_err2() { + // error condition 2 -- offset too large + evm_unit_test! { + (m) { + RETURNDATACOPY; + } + m.state.return_data = vec![0x00, 0x01, 0x02]; + m.state.stack.push(U256::from(2)).unwrap(); // length + m.state.stack.push(U256::from(10)).unwrap(); // offset + m.state.stack.push(U256::from(0)).unwrap(); // dest-offset + let result = m.step(); + assert!(result.is_err(), "execution step succeeded"); + assert_eq!(result.err().unwrap().exit_code(), EVM_CONTRACT_ILLEGAL_MEMORY_ACCESS); + }; + } + + #[test] + fn test_returndatacopy_err3() { + // error condition 3 -- offset+length too large + evm_unit_test! { + (m) { + RETURNDATACOPY; + } + m.state.return_data = vec![0x00, 0x01, 0x02]; + m.state.stack.push(U256::from(2)).unwrap(); // length + m.state.stack.push(U256::from(2)).unwrap(); // offset + m.state.stack.push(U256::from(0)).unwrap(); // dest-offset + let result = m.step(); + assert!(result.is_err(), "execution step succeeded"); + assert_eq!(result.err().unwrap().exit_code(), EVM_CONTRACT_ILLEGAL_MEMORY_ACCESS); + }; + } + + #[test] + fn test_return() { + evm_unit_test! { + (m) { + // output data + PUSH4; 0x01; 0x02; 0x03; 0x04; + PUSH0; + MSTORE; + // exit + RETURN; + // correctness check sled + INVALID; + } + m.state.stack.push(U256::from(4)).unwrap(); // length + m.state.stack.push(U256::from(28)).unwrap(); // offset + let result = m.execute().expect("execution failed"); + assert_eq!(result.outcome, crate::Outcome::Return); + let expected_data = vec![0x01, 0x02, 0x03, 0x04]; + assert_eq!(&result.return_data, &expected_data); + }; + } + + #[test] + fn test_revert() { + evm_unit_test! { + (m) { + // output data + PUSH4; 0x01; 0x02; 0x03; 0x04; + PUSH0; + MSTORE; + // exit + REVERT; + // correctness check sled + INVALID; + } + m.state.stack.push(U256::from(4)).unwrap(); // length + m.state.stack.push(U256::from(28)).unwrap(); // offset + let result = m.execute().expect("execution failed"); + assert_eq!(result.outcome, crate::Outcome::Revert); + let expected_data = vec![0x01, 0x02, 0x03, 0x04]; + assert_eq!(&result.return_data, &expected_data); + }; + } + + #[test] + fn test_invalid() { + evm_unit_test! { + (m) { + INVALID; + } + let result = m.step(); + assert!(result.is_err(), "execution succeeded"); + assert_eq!(result.err().unwrap().exit_code(), crate::EVM_CONTRACT_INVALID_INSTRUCTION); + }; + } +} diff --git a/actors/evm/src/interpreter/instructions/ext.rs b/actors/evm/src/interpreter/instructions/ext.rs new file mode 100644 index 000000000..0394e4b30 --- /dev/null +++ b/actors/evm/src/interpreter/instructions/ext.rs @@ -0,0 +1,394 @@ +use crate::interpreter::instructions::memory::copy_to_memory; +use crate::interpreter::precompiles::Precompiles; +use crate::BytecodeHash; +use cid::Cid; +use fil_actors_evm_shared::address::EthAddress; +use fil_actors_evm_shared::uints::U256; +use fil_actors_runtime::runtime::builtins::Type; +use fil_actors_runtime::ActorError; +use fil_actors_runtime::{deserialize_block, AsActorError}; +use fvm_ipld_blockstore::Blockstore; +use fvm_shared::error::ExitCode; +use fvm_shared::sys::SendFlags; +use fvm_shared::{address::Address, econ::TokenAmount}; +use num_traits::Zero; +use { + crate::interpreter::{ExecutionState, System}, + fil_actors_runtime::runtime::Runtime, +}; + +pub fn extcodesize( + _state: &mut ExecutionState, + system: &mut System, + addr: U256, +) -> Result { + // TODO (M2.2) we're fetching the entire block here just to get its size. We should instead use + // the ipld::block_stat syscall, but the Runtime nor the Blockstore expose it. + // Tracked in https://github.com/filecoin-project/ref-fvm/issues/867 + let len = match get_contract_type(system.rt, &addr.into()) { + ContractType::EVM(addr) => { + get_evm_bytecode(system, &addr).map(|bytecode| bytecode.len())? + } + ContractType::Native(_) => 1, + // account, not found, and precompiles are 0 size + _ => 0, + }; + + Ok(len.into()) +} + +pub fn extcodehash( + _state: &mut ExecutionState, + system: &mut System, + addr: U256, +) -> Result { + let addr = match get_contract_type(system.rt, &addr.into()) { + ContractType::EVM(a) => a, + // _Technically_ since we have native "bytecode" set as 0xfe this is valid, though we cant differentiate between different native actors. + ContractType::Native(_) => return Ok(BytecodeHash::NATIVE_ACTOR.into()), + // Precompiles "exist" and therefore aren't empty (although spec-wise they can be either 0 or keccak("") ). + ContractType::Precompile => return Ok(BytecodeHash::EMPTY.into()), + // NOTE: There may be accounts that in EVM would be considered "empty" (as defined in EIP-161) and give 0, but we will instead return keccak(""). + // The FVM does not have chain state cleanup so contracts will never end up "empty" and be removed, they will either exist (in any state in the contract lifecycle) + // and return keccak(""), or not exist (where nothing has ever been deployed at that address) and return 0. + // TODO: With account abstraction, this may be something other than an empty hash! + ContractType::Account => return Ok(BytecodeHash::EMPTY.into()), + // Not found + ContractType::NotFound => return Ok(U256::zero()), + }; + + // multihash { keccak256(bytecode) } + let bytecode_hash: BytecodeHash = deserialize_block(system.send( + &addr, + crate::Method::GetBytecodeHash as u64, + Default::default(), + TokenAmount::zero(), + None, + SendFlags::READ_ONLY, + )?)?; + Ok(bytecode_hash.into()) +} + +pub fn extcodecopy( + state: &mut ExecutionState, + system: &mut System, + addr: U256, + dest_offset: U256, + data_offset: U256, + size: U256, +) -> Result<(), ActorError> { + let bytecode = match get_contract_type(system.rt, &addr.into()) { + ContractType::EVM(addr) => get_evm_bytecode(system, &addr)?, + ContractType::NotFound | ContractType::Account | ContractType::Precompile => Vec::new(), + // calling EXTCODECOPY on native actors results with a single byte 0xFE which solidtiy uses for its `assert`/`throw` methods + // and in general invalid EVM bytecode + _ => vec![0xFE], + }; + + copy_to_memory(&mut state.memory, dest_offset, size, data_offset, bytecode.as_slice(), true) +} + +#[derive(Debug)] +#[allow(clippy::upper_case_acronyms)] +pub enum ContractType { + Precompile, + /// EVM ID Address and the CID of the actor (not the bytecode) + EVM(Address), + Native(Cid), + Account, + NotFound, +} + +/// Resolves an address to the address type +pub fn get_contract_type(rt: &RT, addr: &EthAddress) -> ContractType { + // precompiles cant be resolved by the FVM + // addresses passed in precompile range will be returned as NotFound; EAM asserts that no actors can be deployed in the precompile reserved range + if Precompiles::::is_precompile(addr) { + return ContractType::Precompile; + } + + let addr: Address = addr.into(); + rt.resolve_address(&addr) // resolve actor id + .and_then(|id| rt.get_actor_code_cid(&id).map(|cid| (id, cid))) // resolve code cid + .map(|(id, cid)| match rt.resolve_builtin_actor_type(&cid) { + // TODO part of current account abstraction hack where placeholders are accounts + Some(Type::Account | Type::Placeholder | Type::EthAccount) => ContractType::Account, + Some(Type::EVM) => ContractType::EVM(Address::new_id(id)), + // remaining builtin actors are native + _ => ContractType::Native(cid), + }) + .unwrap_or(ContractType::NotFound) +} + +pub fn get_evm_bytecode_cid( + system: &mut System, + addr: &Address, +) -> Result, ActorError> { + deserialize_block(system.send( + addr, + crate::Method::GetBytecode as u64, + Default::default(), + TokenAmount::zero(), + None, + SendFlags::READ_ONLY, + )?) +} + +pub fn get_evm_bytecode( + system: &mut System, + addr: &Address, +) -> Result, ActorError> { + if let Some(cid) = get_evm_bytecode_cid(system, addr)? { + let raw_bytecode = system + .rt + .store() + .get(&cid) // TODO this is inefficient; should call stat here. + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to get bytecode block")? + .context_code(ExitCode::USR_ILLEGAL_STATE, "bytecode block not found")?; + Ok(raw_bytecode) + } else { + Ok(Vec::new()) + } +} + +#[cfg(test)] +mod tests { + use crate::evm_unit_test; + use crate::BytecodeHash; + use cid::Cid; + use fil_actors_evm_shared::uints::U256; + use fil_actors_runtime::runtime::Primitives; + use fil_actors_runtime::test_utils::EVM_ACTOR_CODE_ID; + use fvm_ipld_blockstore::Blockstore; + use fvm_ipld_encoding::ipld_block::IpldBlock; + use fvm_shared::address::Address as FilAddress; + use fvm_shared::crypto::hash::SupportedHashes; + use fvm_shared::error::ExitCode; + use fvm_shared::sys::SendFlags; + use num_traits::Zero; + + #[test] + fn test_extcodesize() { + evm_unit_test! { + (rt) { + rt.in_call = true; + + let addr = FilAddress::new_id(1001); + rt.set_address_actor_type(addr, *EVM_ACTOR_CODE_ID); + + let bytecode_cid = Cid::try_from("baeaikaia").unwrap(); + let bytecode = vec![0x01, 0x02, 0x03, 0x04]; + rt.store.put_keyed(&bytecode_cid, bytecode.as_slice()).unwrap(); + + rt.expect_send( + addr, + crate::Method::GetBytecode as u64, + Default::default(), + TokenAmount::zero(), + None, + SendFlags::READ_ONLY, + IpldBlock::serialize_cbor(&bytecode_cid).unwrap(), + ExitCode::OK, + None, + ); + } + (m) { + EXTCODESIZE; + } + m.state.stack.push(EthAddress::from_id(1001).as_evm_word()).unwrap(); + let result = m.step(); + assert!(result.is_ok(), "execution step failed"); + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), U256::from(4)); + }; + } + + #[test] + fn test_extcodesize_nonexist() { + evm_unit_test! { + (rt) { + rt.in_call = true; + } + (m) { + EXTCODESIZE; + } + m.state.stack.push(EthAddress::from_id(1001).as_evm_word()).unwrap(); + let result = m.step(); + assert!(result.is_ok(), "execution step failed"); + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), U256::from(0)); + }; + } + + #[test] + fn test_extcodecopy() { + let bytecode = vec![0x01, 0x02, 0x03, 0x04]; + + evm_unit_test! { + (rt) { + rt.in_call = true; + + let addr = FilAddress::new_id(1001); + rt.set_address_actor_type(addr, *EVM_ACTOR_CODE_ID); + let bytecode_cid = Cid::try_from("baeaikaia").unwrap(); + rt.store.put_keyed(&bytecode_cid, bytecode.as_slice()).unwrap(); + + rt.expect_send( + addr, + crate::Method::GetBytecode as u64, + Default::default(), + TokenAmount::zero(), + None, + SendFlags::READ_ONLY, + IpldBlock::serialize_cbor(&bytecode_cid).unwrap(), + ExitCode::OK, + None, + ); + } + (m) { + EXTCODECOPY; + } + m.state.stack.push(U256::from(4)).unwrap(); // length + m.state.stack.push(U256::from(0)).unwrap(); // offset + m.state.stack.push(U256::from(0)).unwrap(); // destOffset + m.state.stack.push(EthAddress::from_id(1001).as_evm_word()).unwrap(); + let result = m.step(); + assert!(result.is_ok(), "execution step failed"); + assert_eq!(m.state.stack.len(), 0); + assert_eq!(&m.state.memory[0..4], &bytecode); + }; + } + + #[test] + fn test_extcodecopy_partial() { + let bytecode = vec![0x01, 0x02, 0x03, 0x04]; + + evm_unit_test! { + (rt) { + rt.in_call = true; + + let addr = FilAddress::new_id(1001); + rt.set_address_actor_type(addr, *EVM_ACTOR_CODE_ID); + let bytecode_cid = Cid::try_from("baeaikaia").unwrap(); + rt.store.put_keyed(&bytecode_cid, bytecode.as_slice()).unwrap(); + + rt.expect_send( + addr, + crate::Method::GetBytecode as u64, + Default::default(), + TokenAmount::zero(), + None, + SendFlags::READ_ONLY, + IpldBlock::serialize_cbor(&bytecode_cid).unwrap(), + ExitCode::OK, + None, + ); + } + (m) { + EXTCODECOPY; + } + m.state.stack.push(U256::from(3)).unwrap(); // length + m.state.stack.push(U256::from(1)).unwrap(); // offset + m.state.stack.push(U256::from(0)).unwrap(); // destOffset + m.state.stack.push(EthAddress::from_id(1001).as_evm_word()).unwrap(); + let result = m.step(); + assert!(result.is_ok(), "execution step failed"); + assert_eq!(m.state.stack.len(), 0); + assert_eq!(m.state.memory[0..3], bytecode[1..4]); + }; + } + + #[test] + fn test_extcodecopy_oob() { + let bytecode = vec![0x01, 0x02, 0x03, 0x04]; + + evm_unit_test! { + (rt) { + rt.in_call = true; + + let addr = FilAddress::new_id(1001); + rt.set_address_actor_type(addr, *EVM_ACTOR_CODE_ID); + let bytecode_cid = Cid::try_from("baeaikaia").unwrap(); + rt.store.put_keyed(&bytecode_cid, bytecode.as_slice()).unwrap(); + + rt.expect_send( + addr, + crate::Method::GetBytecode as u64, + Default::default(), + TokenAmount::zero(), + None, + SendFlags::READ_ONLY, + IpldBlock::serialize_cbor(&bytecode_cid).unwrap(), + ExitCode::OK, + None, + ); + } + (m) { + EXTCODECOPY; + } + m.state.stack.push(U256::from(4)).unwrap(); // length + m.state.stack.push(U256::from(1)).unwrap(); // offset + m.state.stack.push(U256::from(0)).unwrap(); // destOffset + m.state.stack.push(EthAddress::from_id(1001).as_evm_word()).unwrap(); + let result = m.step(); + assert!(result.is_ok(), "execution step failed"); + assert_eq!(m.state.stack.len(), 0); + assert_eq!(m.state.memory[0..3], bytecode[1..4]); + }; + } + + #[test] + fn test_extcodehash() { + #[allow(unused_assignments)] + let mut bytecode_hash = None; + + evm_unit_test! { + (rt) { + rt.in_call = true; + + let addr = FilAddress::new_id(1001); + rt.set_address_actor_type(addr, *EVM_ACTOR_CODE_ID); + let bytecode = vec![0x01, 0x02, 0x03, 0x04]; + let hash = BytecodeHash::try_from(rt.hash(SupportedHashes::Keccak256, &bytecode).as_slice()).unwrap(); + bytecode_hash = Some(hash); + + rt.expect_send( + addr, + crate::Method::GetBytecodeHash as u64, + Default::default(), + TokenAmount::zero(), + None, + SendFlags::READ_ONLY, + IpldBlock::serialize_cbor(&hash).unwrap(), + ExitCode::OK, + None, + ); + } + (m) { + EXTCODEHASH; + } + m.state.stack.push(EthAddress::from_id(1001).as_evm_word()).unwrap(); + let result = m.step(); + assert!(result.is_ok(), "execution step failed"); + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), U256::from(bytecode_hash.unwrap())); + }; + } + + #[test] + fn test_extcodehash_nonexist() { + evm_unit_test! { + (rt) { + rt.in_call = true; + } + (m) { + EXTCODEHASH; + } + m.state.stack.push(EthAddress::from_id(1001).as_evm_word()).unwrap(); + let result = m.step(); + assert!(result.is_ok(), "execution step failed"); + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), U256::from(0)); + }; + } +} diff --git a/actors/evm/src/interpreter/instructions/hash.rs b/actors/evm/src/interpreter/instructions/hash.rs new file mode 100644 index 000000000..e6c803725 --- /dev/null +++ b/actors/evm/src/interpreter/instructions/hash.rs @@ -0,0 +1,89 @@ +use fil_actors_evm_shared::uints::U256; +use fil_actors_runtime::ActorError; + +use { + super::memory::get_memory_region, + crate::interpreter::{ExecutionState, System}, + fil_actors_runtime::runtime::Runtime, + fvm_shared::crypto::hash::SupportedHashes, +}; + +pub fn keccak256( + state: &mut ExecutionState, + system: &System, + index: U256, + size: U256, +) -> Result { + let region = get_memory_region(&mut state.memory, index, size)?; + + let (buf, size) = system.rt.hash_64( + SupportedHashes::Keccak256, + if let Some(region) = region { + &state.memory[region.offset..region.offset + region.size.get()] + } else { + &[] + }, + ); + + Ok(U256::from_big_endian(&buf[..size])) +} + +#[cfg(test)] +mod test { + use fil_actors_evm_shared::uints::U256; + use fil_actors_runtime::runtime::Primitives; + + use crate::{evm_unit_test, BytecodeHash}; + + #[test] + fn keccak256_large() { + for i in 0..12 { + let len = 1 << i; // 1 through 2^11 bytes length + println!("{}", len); + let v = vec![0xff; len]; + let [a, b] = u16::try_from(len).unwrap().to_be_bytes(); + evm_unit_test! { + (m) { + PUSH2; + {a}; + {b}; + PUSH0; + KECCAK256; + } + + let expect = &m.system.rt.hash_64(fvm_shared::crypto::hash::SupportedHashes::Keccak256, &v).0[..32]; + + m.state.memory.grow(len); + m.state.memory[..len].copy_from_slice(&v); + m.step().expect("execution step failed"); + m.step().expect("execution step failed"); + m.step().expect("execution step failed"); + + assert_eq!(m.state.stack.pop().unwrap(), U256::from(expect)); + }; + } + } + + #[test] + fn keccak256_ext() { + for (input, expect) in + [([0xfe].as_slice(), BytecodeHash::NATIVE_ACTOR), (&[], BytecodeHash::EMPTY)] + { + evm_unit_test! { + (m) { + PUSH1; + {input.len() as u8}; + PUSH0; + KECCAK256; + } + m.state.memory.grow(input.len()); + m.state.memory[..input.len()].copy_from_slice(input); + m.step().expect("execution step failed"); + m.step().expect("execution step failed"); + m.step().expect("execution step failed"); + + assert_eq!(m.state.stack.pop().unwrap(), U256::from(expect)); + }; + } + } +} diff --git a/actors/evm/src/interpreter/instructions/lifecycle.rs b/actors/evm/src/interpreter/instructions/lifecycle.rs new file mode 100644 index 000000000..4cad01b86 --- /dev/null +++ b/actors/evm/src/interpreter/instructions/lifecycle.rs @@ -0,0 +1,584 @@ +use fil_actors_evm_shared::address::EthAddress; +use fil_actors_evm_shared::uints::U256; +use fil_actors_runtime::ActorError; +use fil_actors_runtime::EAM_ACTOR_ADDR; +use fil_actors_runtime::{deserialize_block, extract_send_result}; +use fvm_ipld_encoding::ipld_block::IpldBlock; +use fvm_shared::sys::SendFlags; +use fvm_shared::MethodNum; +use fvm_shared::METHOD_SEND; +use fvm_shared::{address::Address, econ::TokenAmount}; + +use crate::ext::eam; +use crate::interpreter::Output; +use crate::EVM_CONTRACT_SELFDESTRUCT_FAILED; + +use super::memory::{get_memory_region, MemoryRegion}; +use { + crate::interpreter::{ExecutionState, System}, + fil_actors_runtime::runtime::Runtime, +}; + +#[inline] +pub fn create( + state: &mut ExecutionState, + system: &mut System, + value: U256, + offset: U256, + size: U256, +) -> Result { + if system.readonly { + return Err(ActorError::read_only("create called while read-only".into())); + } + + let ExecutionState { stack: _, memory, .. } = state; + + let value = TokenAmount::from(&value); + if value > system.rt.current_balance() { + return Ok(U256::zero()); + } + let input_region = get_memory_region(memory, offset, size)?; + + let input_data = if let Some(MemoryRegion { offset, size }) = input_region { + &memory[offset..][..size.get()] + } else { + &[] + }; + + // We increment the nonce earlier than in the EVM. See the comment in `create2` for details. + let nonce = system.increment_nonce(); + let params = eam::CreateParams { code: input_data.to_vec(), nonce }; + create_init(system, IpldBlock::serialize_cbor(¶ms)?, eam::CREATE_METHOD_NUM, value) +} + +pub fn create2( + state: &mut ExecutionState, + system: &mut System, + endowment: U256, + offset: U256, + size: U256, + salt: U256, +) -> Result { + if system.readonly { + return Err(ActorError::read_only("create2 called while read-only".into())); + } + + let ExecutionState { stack: _, memory, .. } = state; + + let endowment = TokenAmount::from(&endowment); + if endowment > system.rt.current_balance() { + return Ok(U256::zero()); + } + + let input_region = get_memory_region(memory, offset, size)?; + + // BE encoded array + let salt: [u8; 32] = salt.into(); + + let input_data = if let Some(MemoryRegion { offset, size }) = input_region { + &memory[offset..][..size.get()] + } else { + &[] + }; + let params = eam::Create2Params { code: input_data.to_vec(), salt }; + + // We increment the nonce earlier than in the EVM, but this is unlikely to cause issues: + // + // 1. Like the EVM, we increment the nonce on address conflict (effectively "skipping" the + // address). + // 2. Like the EVM, we increment the nonce even if the target contract runs out of gas. + // 4. Like the EVM, we don't increment the nonce if the caller doesn't have enough funds to + // cover the endowment. + // 4. Unlike the EVM, we increment the nonce if contract creation fails because we're at the max + // stack depth. However, given that there are other ways to increment the nonce without + // deploying a contract (e.g., 2), this shouldn't be an issue. + system.increment_nonce(); + create_init(system, IpldBlock::serialize_cbor(¶ms)?, eam::CREATE2_METHOD_NUM, endowment) +} + +/// call into Ethereum Address Manager to make the new account +#[inline] +fn create_init( + system: &mut System, + params: Option, + method: MethodNum, + value: TokenAmount, +) -> Result { + // Apply EIP-150 + let gas_limit = (63 * system.rt.gas_available()) / 64; + + // send bytecode & params to EAM to generate the address and contract + let ret = + system.send(&EAM_ACTOR_ADDR, method, params, value, Some(gas_limit), SendFlags::default()); + + Ok(match ret { + Ok(eam_ret) => { + let ret: eam::CreateReturn = deserialize_block(eam_ret)?; + ret.eth_address.as_evm_word() + } + Err(_) => U256::zero(), + }) +} + +#[inline] +pub fn selfdestruct( + _state: &mut ExecutionState, + system: &mut System, + beneficiary: U256, +) -> Result { + use crate::interpreter::output::Outcome; + + if system.readonly { + return Err(ActorError::read_only("selfdestruct called while read-only".into())); + } + + // Try to give funds to the beneficiary. If this fails, we abort the entire call. This can only + // fail if: + // + // 1. The target address is an embedded ID address and said actor doesn't exist. + // 2. We're at the maximum call depth. + // 3. This call would cause us to exceed some system limit (e.g., a memory limit). + let beneficiary: EthAddress = beneficiary.into(); + let beneficiary: Address = beneficiary.into(); + let balance = system.rt.current_balance(); + extract_send_result(system.rt.send_simple(&beneficiary, METHOD_SEND, None, balance)).map_err( + |e| { + ActorError::unchecked( + EVM_CONTRACT_SELFDESTRUCT_FAILED, + format!( + "failed to transfer funds to beneficiary {beneficiary} on SELFDESTRUCT: {e}" + ), + ) + }, + )?; + + // Now mark ourselves as deleted. + system.mark_selfdestructed(); + + // And "return". + // + // 1. In the constructor, this will set our code to "empty". This is correct. + // 2. Otherwise, we'll successfully return nothing to the caller. + Ok(Output { outcome: Outcome::Return, return_data: Vec::new() }) +} + +#[cfg(test)] +mod tests { + use crate::evm_unit_test; + use crate::ext::eam; + + use fil_actors_evm_shared::uints::U256; + use fil_actors_runtime::EAM_ACTOR_ADDR; + use fvm_ipld_encoding::ipld_block::IpldBlock; + use fvm_shared::address::Address as FilAddress; + use fvm_shared::error::{ErrorNumber, ExitCode}; + use fvm_shared::sys::SendFlags; + use fvm_shared::METHOD_SEND; + + #[test] + fn test_create() { + let ret_addr = EthAddress(hex_literal::hex!("CAFEB0BA00000000000000000000000000000000")); + + evm_unit_test! { + (rt) { + rt.set_balance(TokenAmount::from_atto(1_000_000)); + + let code = vec![0x01, 0x02, 0x03, 0x04]; + let nonce = 1; + let create_params = eam::CreateParams { code, nonce }; + let create_ret = eam::CreateReturn { + actor_id: 12345, + eth_address: ret_addr, + robust_address: Some((&ret_addr).try_into().unwrap()), + }; + + rt.expect_gas_available(10_000_000_000); + rt.expect_send( + EAM_ACTOR_ADDR, + eam::CREATE_METHOD_NUM, + IpldBlock::serialize_cbor(&create_params).unwrap(), + TokenAmount::from_atto(1234), + Some(63 * 10_000_000_000 / 64), + SendFlags::empty(), + IpldBlock::serialize_cbor(&create_ret).unwrap(), + ExitCode::OK, + None, + ); + } + (m) { + // input data + PUSH4; 0x01; 0x02; 0x03; 0x04; + PUSH0; + MSTORE; + // the deed + CREATE; + } + m.state.stack.push(U256::from(4)).unwrap(); // input size + m.state.stack.push(U256::from(28)).unwrap(); // input offset + m.state.stack.push(U256::from(1234)).unwrap(); // initial value + for _ in 0..4 { + m.step().expect("execution step failed"); + } + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), ret_addr.as_evm_word()); + assert_eq!(m.system.nonce, 2); + }; + } + + #[test] + fn test_create2() { + let ret_addr = EthAddress(hex_literal::hex!("CAFEB0BA00000000000000000000000000000000")); + + evm_unit_test! { + (rt) { + rt.set_balance(TokenAmount::from_atto(1_000_000)); + + let code = vec![0x01, 0x02, 0x03, 0x04]; + let mut salt = [0u8; 32]; + salt[28] = 0xDE; + salt[29] = 0xAD; + salt[30] = 0xBE; + salt[31] = 0xEF; + let create_params = eam::Create2Params { code, salt }; + let create_ret = eam::CreateReturn { + actor_id: 12345, + eth_address: ret_addr, + robust_address: Some((&ret_addr).try_into().unwrap()), + }; + + rt.expect_gas_available(10_000_000_000); + rt.expect_send( + EAM_ACTOR_ADDR, + eam::CREATE2_METHOD_NUM, + IpldBlock::serialize_cbor(&create_params).unwrap(), + TokenAmount::from_atto(1234), + Some(63 * 10_000_000_000 / 64), + SendFlags::empty(), + IpldBlock::serialize_cbor(&create_ret).unwrap(), + ExitCode::OK, + None, + ); + } + (m) { + // input data + PUSH4; 0x01; 0x02; 0x03; 0x04; + PUSH0; + MSTORE; + // the deed + CREATE2; + } + m.state.stack.push(U256::from(0xDEADBEEFu64)).unwrap(); // salt + m.state.stack.push(U256::from(4)).unwrap(); // input size + m.state.stack.push(U256::from(28)).unwrap(); // input offset + m.state.stack.push(U256::from(1234)).unwrap(); // initial value + for _ in 0..4 { + m.step().expect("execution step failed"); + } + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), ret_addr.as_evm_word()); + assert_eq!(m.system.nonce, 2); + }; + } + + #[test] + fn test_create_fail_eam() { + // this covers the relevant create2 codepath as well (in create_init) + evm_unit_test! { + (rt) { + rt.set_balance(TokenAmount::from_atto(1_000_000)); + + let code = vec![0x01, 0x02, 0x03, 0x04]; + let nonce = 1; + let create_params = eam::CreateParams { code, nonce }; + + rt.expect_gas_available(10_000_000_000); + rt.expect_send( + EAM_ACTOR_ADDR, + eam::CREATE_METHOD_NUM, + IpldBlock::serialize_cbor(&create_params).unwrap(), + TokenAmount::from_atto(1234), + Some(63 * 10_000_000_000 / 64), + SendFlags::empty(), + None, + ExitCode::USR_FORBIDDEN, + None, + ); + } + (m) { + // input data + PUSH4; 0x01; 0x02; 0x03; 0x04; + PUSH0; + MSTORE; + // the deed + CREATE; + } + m.state.stack.push(U256::from(4)).unwrap(); // input size + m.state.stack.push(U256::from(28)).unwrap(); // input offset + m.state.stack.push(U256::from(1234)).unwrap(); // initial value + for _ in 0..4 { + m.step().expect("execution step failed"); + } + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), U256::from(0)); + assert_eq!(m.system.nonce, 2); + }; + } + + #[test] + fn test_create_fail_nofunds() { + evm_unit_test! { + (rt) { + rt.set_balance(TokenAmount::from_atto(1)); + } + (m) { + // input data + PUSH4; 0x01; 0x02; 0x03; 0x04; + PUSH0; + MSTORE; + // the deed + CREATE; + } + m.state.stack.push(U256::from(4)).unwrap(); // input size + m.state.stack.push(U256::from(28)).unwrap(); // input offset + m.state.stack.push(U256::from(1234)).unwrap(); // initial value + for _ in 0..4 { + m.step().expect("execution step failed"); + } + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), U256::from(0)); + assert_eq!(m.system.nonce, 1); + }; + } + + #[test] + fn test_create2_fail_nofunds() { + evm_unit_test! { + (rt) { + rt.set_balance(TokenAmount::from_atto(1)); + } + (m) { + // input data + PUSH4; 0x01; 0x02; 0x03; 0x04; + PUSH0; + MSTORE; + // the deed + CREATE2; + } + m.state.stack.push(U256::from(0xDEADBEEFu64)).unwrap(); // salt + m.state.stack.push(U256::from(4)).unwrap(); // input size + m.state.stack.push(U256::from(28)).unwrap(); // input offset + m.state.stack.push(U256::from(1234)).unwrap(); // initial value + for _ in 0..4 { + m.step().expect("execution step failed"); + } + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), U256::from(0)); + assert_eq!(m.system.nonce, 1); + }; + } + + #[test] + fn test_create_fail_readonly() { + evm_unit_test! { + (rt) { + rt.set_balance(TokenAmount::from_atto(1)); + } + (m) { + // input data + PUSH4; 0x01; 0x02; 0x03; 0x04; + PUSH0; + MSTORE; + // the deed + CREATE; + } + m.system.readonly = true; + m.state.stack.push(U256::from(4)).unwrap(); // input size + m.state.stack.push(U256::from(28)).unwrap(); // input offset + m.state.stack.push(U256::from(1234)).unwrap(); // initial value + for _ in 0..3 { + m.step().expect("execution step failed"); + } + let result = m.step(); + assert!(result.is_err()); + assert_eq!(result.err().unwrap().exit_code(), ExitCode::USR_READ_ONLY); + assert_eq!(m.system.nonce, 1); + }; + } + + #[test] + fn test_create2_fail_readonly() { + evm_unit_test! { + (rt) { + rt.set_balance(TokenAmount::from_atto(1)); + } + (m) { + // input data + PUSH4; 0x01; 0x02; 0x03; 0x04; + PUSH0; + MSTORE; + // the deed + CREATE2; + } + m.system.readonly = true; + m.state.stack.push(U256::from(0xDEADBEEFu64)).unwrap(); // salt + m.state.stack.push(U256::from(4)).unwrap(); // input size + m.state.stack.push(U256::from(28)).unwrap(); // input offset + m.state.stack.push(U256::from(1234)).unwrap(); // initial value + for _ in 0..3 { + m.step().expect("execution step failed"); + } + let result = m.step(); + assert!(result.is_err()); + assert_eq!(result.err().unwrap().exit_code(), ExitCode::USR_READ_ONLY); + assert_eq!(m.system.nonce, 1); + }; + } + + #[test] + fn test_create_err() { + // this covers the relevant create2 codepath as well (in create_init) + evm_unit_test! { + (rt) { + rt.set_balance(TokenAmount::from_atto(1_000_000)); + + let code = vec![0x01, 0x02, 0x03, 0x04]; + let nonce = 1; + let create_params = eam::CreateParams { code, nonce }; + + rt.expect_gas_available(10_000_000_000); + rt.expect_send( + EAM_ACTOR_ADDR, + eam::CREATE_METHOD_NUM, + IpldBlock::serialize_cbor(&create_params).unwrap(), + TokenAmount::from_atto(1234), + Some(63 * 10_000_000_000 / 64), + SendFlags::empty(), + None, + ExitCode::OK, + Some(ErrorNumber::IllegalOperation), + ); + } + (m) { + // input data + PUSH4; 0x01; 0x02; 0x03; 0x04; + PUSH0; + MSTORE; + // the deed + CREATE; + } + m.state.stack.push(U256::from(4)).unwrap(); // input size + m.state.stack.push(U256::from(28)).unwrap(); // input offset + m.state.stack.push(U256::from(1234)).unwrap(); // initial value + for _ in 0..4 { + m.step().expect("execution step failed"); + } + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), U256::from(0)); + assert_eq!(m.system.nonce, 2); + }; + } + + #[test] + fn test_selfdestruct() { + // tests the outcome of selfdestruct + let beneficiary = EthAddress::from_id(1001); + let fil_beneficiary = FilAddress::new_id(1001); + + evm_unit_test! { + (rt) { + rt.set_balance(TokenAmount::from_atto(1_000_000)); + + rt.expect_send( + fil_beneficiary, + METHOD_SEND, + None, + TokenAmount::from_atto(1_000_000), + None, + SendFlags::empty(), + None, + ExitCode::OK, + None, + ); + } + (m) { + SELFDESTRUCT; + // correctness check sled + INVALID; + } + m.state.stack.push(beneficiary.as_evm_word()).unwrap(); + let result = m.execute().expect("execution failed"); + assert_eq!(result.outcome, crate::Outcome::Return); + assert_eq!(result.return_data.len(), 0); + } + } + + #[test] + fn test_selfdestruct_tombstone() { + // tests the selfdestruct step to ensure a tombstone is created. + // can't merge these two tests because rust.... + let beneficiary = EthAddress::from_id(1001); + let fil_beneficiary = FilAddress::new_id(1001); + + evm_unit_test! { + (rt) { + rt.set_balance(TokenAmount::from_atto(1_000_000)); + rt.set_origin(fil_beneficiary); + + rt.expect_send( + fil_beneficiary, + METHOD_SEND, + None, + TokenAmount::from_atto(1_000_000), + None, + SendFlags::empty(), + None, + ExitCode::OK, + None, + ); + } + (m) { + SELFDESTRUCT; + } + m.state.stack.push(beneficiary.as_evm_word()).unwrap(); + m.step().expect("execution step failed"); + assert!(m.system.tombstone.is_some()); + assert_eq!(m.system.tombstone.unwrap(), + crate::Tombstone { + origin: 1001, + nonce: 0, + }); + } + } + + #[test] + fn test_selfdestruct_fail() { + // tests the outcome of selfdestruct + let beneficiary = EthAddress::from_id(1001); + let fil_beneficiary = FilAddress::new_id(1001); + + evm_unit_test! { + (rt) { + rt.set_balance(TokenAmount::from_atto(1_000_000)); + + rt.expect_send( + fil_beneficiary, + METHOD_SEND, + None, + TokenAmount::from_atto(1_000_000), + None, + SendFlags::empty(), + None, + ExitCode::USR_FORBIDDEN, + None, + ); + } + (m) { + SELFDESTRUCT; + } + m.state.stack.push(beneficiary.as_evm_word()).unwrap(); + let result = m.step(); + assert!(result.is_err()); + assert_eq!(result.err().unwrap().exit_code(), crate::EVM_CONTRACT_SELFDESTRUCT_FAILED); + } + } +} diff --git a/actors/evm/src/interpreter/instructions/log_event.rs b/actors/evm/src/interpreter/instructions/log_event.rs new file mode 100644 index 000000000..e175f83fa --- /dev/null +++ b/actors/evm/src/interpreter/instructions/log_event.rs @@ -0,0 +1,325 @@ +use crate::interpreter::instructions::memory::get_memory_region; +use fil_actors_evm_shared::uints::U256; +use fil_actors_runtime::ActorError; +use fvm_ipld_encoding::IPLD_RAW; +use fvm_shared::event::{Entry, Flags}; +use { + crate::interpreter::{ExecutionState, System}, + fil_actors_runtime::runtime::Runtime, +}; + +/// The event key for the Ethereum log data. +const EVENT_DATA_KEY: &str = "d"; + +/// The event keys for the Ethereum log topics. +const EVENT_TOPIC_KEYS: &[&str] = &["t1", "t2", "t3", "t4"]; + +#[inline] +pub fn log( + state: &mut ExecutionState, + system: &System, + num_topics: usize, + mem_index: U256, + size: U256, + topics: &[U256], +) -> Result<(), ActorError> { + if system.readonly { + return Err(ActorError::read_only("log called while read-only".into())); + } + + // Handle the data. + // Passing in a zero-sized memory region omits the data key entirely. + // LOG0 + a zero-sized memory region emits an event with no entries whatsoever. In this case, + // the FVM will record a hollow event carrying only the emitter actor ID. + let region = get_memory_region(&mut state.memory, mem_index, size)?; + + // Extract the topics. Prefer to allocate an extra item than to incur in the cost of a + // decision based on the size of the data. + let mut entries: Vec = Vec::with_capacity(num_topics + 1); + for i in 0..num_topics { + let key = EVENT_TOPIC_KEYS[i]; + let topic = topics[i]; + let entry = Entry { + flags: Flags::FLAG_INDEXED_ALL, + key: (*key).to_owned(), + codec: IPLD_RAW, + value: topic.to_bytes().into(), // U256 serializes as a byte string. + }; + entries.push(entry); + } + + // Skip adding the data if it's zero-sized. + if let Some(r) = region { + let data = state.memory[r.offset..r.offset + r.size.get()].to_vec(); + let entry = Entry { + flags: Flags::FLAG_INDEXED_ALL, + key: EVENT_DATA_KEY.to_owned(), + codec: IPLD_RAW, + value: data, + }; + entries.push(entry); + } + + system.rt.emit_event(&entries.into())?; + + Ok(()) +} + +#[cfg(test)] +mod tests { + use fil_actors_evm_shared::uints::U256; + use fvm_ipld_encoding::IPLD_RAW; + use fvm_shared::event::{ActorEvent, Entry, Flags}; + + use super::{EVENT_DATA_KEY, EVENT_TOPIC_KEYS}; + use crate::evm_unit_test; + + #[test] + fn test_log0() { + evm_unit_test! { + (rt) { + let mut data = [0u8; 32]; + data[28] = 0xCA; + data[29] = 0xFE; + data[30] = 0xBA; + data[31] = 0xBE; + rt.expect_emitted_event( + ActorEvent::from(vec![Entry{ + flags: Flags::FLAG_INDEXED_ALL, + key: EVENT_DATA_KEY.to_owned(), + codec: IPLD_RAW, + value: data.into(), + }]) + ); + } + (m) { + PUSH4; 0xCA; 0xFE; 0xBA; 0xBE; + PUSH0; + MSTORE; + PUSH1; 0x20; + PUSH0; + LOG0; + } + + let result = m.execute(); + assert!(result.is_ok(), "execution step failed"); + }; + } + + #[test] + fn test_log1() { + evm_unit_test! { + (rt) { + let t1 = U256::from(0x01); + let mut data = [0u8; 32]; + data[28] = 0xCA; + data[29] = 0xFE; + data[30] = 0xBA; + data[31] = 0xBE; + rt.expect_emitted_event( + ActorEvent::from(vec![ + Entry{ + flags: Flags::FLAG_INDEXED_ALL, + key: EVENT_TOPIC_KEYS[0].to_owned(), + codec: IPLD_RAW, + value: t1.to_bytes().into(), + }, + Entry{ + flags: Flags::FLAG_INDEXED_ALL, + key: EVENT_DATA_KEY.to_owned(), + codec: IPLD_RAW, + value: data.into(), + } + ]) + ); + } + (m) { + PUSH4; 0xCA; 0xFE; 0xBA; 0xBE; + PUSH0; + MSTORE; + PUSH1; 0x01; + PUSH1; 0x20; + PUSH0; + LOG1; + } + + let result = m.execute(); + assert!(result.is_ok(), "execution step failed"); + }; + } + + #[test] + fn test_log2() { + evm_unit_test! { + (rt) { + let t1 = U256::from(0x01); + let t2 = U256::from(0x02); + let mut data = [0u8; 32]; + data[28] = 0xCA; + data[29] = 0xFE; + data[30] = 0xBA; + data[31] = 0xBE; + rt.expect_emitted_event( + ActorEvent::from(vec![ + Entry{ + flags: Flags::FLAG_INDEXED_ALL, + key: EVENT_TOPIC_KEYS[0].to_owned(), + codec: IPLD_RAW, + value: t1.to_bytes().into(), + }, + Entry{ + flags: Flags::FLAG_INDEXED_ALL, + key: EVENT_TOPIC_KEYS[1].to_owned(), + codec: IPLD_RAW, + value: t2.to_bytes().into(), + }, + Entry{ + flags: Flags::FLAG_INDEXED_ALL, + key: EVENT_DATA_KEY.to_owned(), + codec: IPLD_RAW, + value: data.into(), + } + ]) + ); + } + (m) { + PUSH4; 0xCA; 0xFE; 0xBA; 0xBE; + PUSH0; + MSTORE; + PUSH1; 0x02; + PUSH1; 0x01; + PUSH1; 0x20; + PUSH0; + LOG2; + } + + let result = m.execute(); + assert!(result.is_ok(), "execution step failed"); + }; + } + + #[test] + fn test_log3() { + evm_unit_test! { + (rt) { + let t1 = U256::from(0x01); + let t2 = U256::from(0x02); + let t3 = U256::from(0x03); + let mut data = [0u8; 32]; + data[28] = 0xCA; + data[29] = 0xFE; + data[30] = 0xBA; + data[31] = 0xBE; + rt.expect_emitted_event( + ActorEvent::from(vec![ + Entry{ + flags: Flags::FLAG_INDEXED_ALL, + key: EVENT_TOPIC_KEYS[0].to_owned(), + codec: IPLD_RAW, + value: t1.to_bytes().into(), + }, + Entry{ + flags: Flags::FLAG_INDEXED_ALL, + key: EVENT_TOPIC_KEYS[1].to_owned(), + codec: IPLD_RAW, + value: t2.to_bytes().into(), + }, + Entry{ + flags: Flags::FLAG_INDEXED_ALL, + key: EVENT_TOPIC_KEYS[2].to_owned(), + codec: IPLD_RAW, + value: t3.to_bytes().into(), + }, + Entry{ + flags: Flags::FLAG_INDEXED_ALL, + key: EVENT_DATA_KEY.to_owned(), + codec: IPLD_RAW, + value: data.into(), + } + ]) + ); + } + (m) { + PUSH4; 0xCA; 0xFE; 0xBA; 0xBE; + PUSH0; + MSTORE; + PUSH1; 0x03; + PUSH1; 0x02; + PUSH1; 0x01; + PUSH1; 0x20; + PUSH0; + LOG3; + } + + let result = m.execute(); + assert!(result.is_ok(), "execution step failed"); + }; + } + + #[test] + fn test_log4() { + evm_unit_test! { + (rt) { + let t1 = U256::from(0x01); + let t2 = U256::from(0x02); + let t3 = U256::from(0x03); + let t4 = U256::from(0x04); + let mut data = [0u8; 32]; + data[28] = 0xCA; + data[29] = 0xFE; + data[30] = 0xBA; + data[31] = 0xBE; + rt.expect_emitted_event( + ActorEvent::from(vec![ + Entry{ + flags: Flags::FLAG_INDEXED_ALL, + key: EVENT_TOPIC_KEYS[0].to_owned(), + codec: IPLD_RAW, + value: t1.to_bytes().into(), + }, + Entry{ + flags: Flags::FLAG_INDEXED_ALL, + key: EVENT_TOPIC_KEYS[1].to_owned(), + codec: IPLD_RAW, + value: t2.to_bytes().into(), + }, + Entry{ + flags: Flags::FLAG_INDEXED_ALL, + key: EVENT_TOPIC_KEYS[2].to_owned(), + codec: IPLD_RAW, + value: t3.to_bytes().into(), + }, + Entry{ + flags: Flags::FLAG_INDEXED_ALL, + key: EVENT_TOPIC_KEYS[3].to_owned(), + codec: IPLD_RAW, + value: t4.to_bytes().into(), + }, + Entry{ + flags: Flags::FLAG_INDEXED_ALL, + key: EVENT_DATA_KEY.to_owned(), + codec: IPLD_RAW, + value: data.into(), + } + ]) + ); + } + + (m) { + PUSH4; 0xCA; 0xFE; 0xBA; 0xBE; + PUSH0; + MSTORE; + PUSH1; 0x04; + PUSH1; 0x03; + PUSH1; 0x02; + PUSH1; 0x01; + PUSH1; 0x20; + PUSH0; + LOG4; + } + + let result = m.execute(); + assert!(result.is_ok(), "execution step failed"); + }; + } +} diff --git a/actors/evm/src/interpreter/instructions/memory.rs b/actors/evm/src/interpreter/instructions/memory.rs new file mode 100644 index 000000000..8e410fe27 --- /dev/null +++ b/actors/evm/src/interpreter/instructions/memory.rs @@ -0,0 +1,600 @@ +#!allow[clippy::result-unit-err] + +use fil_actors_evm_shared::uints::U256; +use fil_actors_runtime::{ActorError, AsActorError}; + +use crate::{EVM_CONTRACT_ILLEGAL_MEMORY_ACCESS, EVM_WORD_SIZE}; + +use { + crate::interpreter::memory::Memory, + crate::interpreter::{ExecutionState, System}, + fil_actors_runtime::runtime::Runtime, + std::num::NonZeroUsize, +}; + +#[derive(Debug)] +pub struct MemoryRegion { + pub offset: usize, + pub size: NonZeroUsize, +} + +#[inline] +pub fn get_memory_region( + mem: &mut Memory, + offset: impl TryInto, + size: impl TryInto, +) -> Result, ActorError> { + // We use u32 because we don't support more than 4GiB of memory anyways. + // Also, explicitly check math so we don't panic and/or wrap around. + let size: u32 = size.try_into().map_err(|_| { + ActorError::unchecked( + EVM_CONTRACT_ILLEGAL_MEMORY_ACCESS, + "size must be less than max u32".into(), + ) + })?; + if size == 0 { + return Ok(None); + } + let offset: u32 = offset.try_into().map_err(|_| { + ActorError::unchecked( + EVM_CONTRACT_ILLEGAL_MEMORY_ACCESS, + "offset must be less than max u32".into(), + ) + })?; + let new_size: u32 = offset + .checked_add(size) + .context_code(EVM_CONTRACT_ILLEGAL_MEMORY_ACCESS, "new memory size exceeds max u32")?; + + mem.grow(new_size as usize); + + Ok(Some(MemoryRegion { + offset: offset as usize, + size: unsafe { NonZeroUsize::new_unchecked(size as usize) }, + })) +} + +pub fn copy_to_memory( + memory: &mut Memory, + dest_offset: U256, + dest_size: U256, + data_offset: U256, + data: &[u8], + zero_fill: bool, +) -> Result<(), ActorError> { + let region = get_memory_region(memory, dest_offset, dest_size)?; + + #[inline(always)] + fn min(a: U256, b: usize) -> usize { + if a < (b as u64) { + a.low_u64() as usize + } else { + b + } + } + + if let Some(region) = ®ion { + let data_len = data.len(); + let data_offset = min(data_offset, data_len); + let copy_size = min(dest_size, data_len - data_offset); + + if copy_size > 0 { + memory[region.offset..region.offset + copy_size] + .copy_from_slice(&data[data_offset..data_offset + copy_size]); + } + + if zero_fill && region.size.get() > copy_size { + memory[region.offset + copy_size..region.offset + region.size.get()].fill(0); + } + } + + Ok(()) +} + +#[inline] +pub fn mload( + state: &mut ExecutionState, + _system: &System, + index: U256, +) -> Result { + let region = get_memory_region(&mut state.memory, index, EVM_WORD_SIZE)?.expect("empty region"); + let value = + U256::from_big_endian(&state.memory[region.offset..region.offset + region.size.get()]); + + Ok(value) +} + +#[inline] +pub fn mstore( + state: &mut ExecutionState, + _system: &System, + index: U256, + value: U256, +) -> Result<(), ActorError> { + let region = get_memory_region(&mut state.memory, index, EVM_WORD_SIZE)?.expect("empty region"); + + let mut bytes = [0u8; EVM_WORD_SIZE]; + value.to_big_endian(&mut bytes); + state.memory[region.offset..region.offset + EVM_WORD_SIZE].copy_from_slice(&bytes); + + Ok(()) +} + +#[inline] +pub fn mstore8( + state: &mut ExecutionState, + _system: &System, + index: U256, + value: U256, +) -> Result<(), ActorError> { + let region = get_memory_region(&mut state.memory, index, 1)?.expect("empty region"); + + let value = (value.low_u32() & 0xff) as u8; + state.memory[region.offset] = value; + + Ok(()) +} + +#[inline] +pub fn msize( + state: &mut ExecutionState, + _system: &System, +) -> Result { + Ok(u64::try_from(state.memory.len()).unwrap().into()) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{evm_unit_test, interpreter::memory::Memory}; + + #[test] + fn copy_to_memory_big() { + let mut mem: Memory = Default::default(); + let result = copy_to_memory( + &mut mem, + U256::zero(), + U256::from(1u128 << 40), + U256::zero(), + &[], + true, + ); + assert_eq!(result.unwrap_err().exit_code(), EVM_CONTRACT_ILLEGAL_MEMORY_ACCESS); + } + + #[test] + fn copy_to_memory_zero() { + let mut mem: Memory = Default::default(); + let result = copy_to_memory( + &mut mem, + U256::zero(), + U256::zero(), + U256::zero(), + &[1u8, 2u8, 3u8], + true, + ); + assert_eq!(result, Ok(())); + assert!(mem.is_empty()); + } + + #[test] + fn copy_to_memory_some() { + let data = &[1u8, 2u8, 3u8]; + let mut mem: Memory = Default::default(); + let result = + copy_to_memory(&mut mem, U256::zero(), U256::from(3), U256::zero(), data, true); + assert_eq!(result, Ok(())); + assert_eq!(mem.len(), 32); + assert_eq!(&mem[0..3], data); + } + + #[test] + fn copy_to_memory_some_truncate() { + let data = &[1u8, 2u8, 3u8, 4u8]; + let result_data = &[1u8, 2u8, 3u8, 0u8]; + + let mut mem: Memory = Default::default(); + let result = + copy_to_memory(&mut mem, U256::zero(), U256::from(3), U256::zero(), data, true); + assert_eq!(result, Ok(())); + assert_eq!(mem.len(), 32); + assert_eq!(&mem[0..4], result_data); + } + + #[test] + fn test_mload_nothing() { + evm_unit_test! { + (m) { + PUSH0; + MLOAD; + } + + m.step().expect("execution step failed"); + m.step().expect("execution step failed"); + + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), U256::zero()); + }; + } + + #[test] + fn test_mload_large_offset() { + evm_unit_test! { + (m) { + PUSH4; // garbage offset + 0x01; + 0x02; + 0x03; + 0x04; + MLOAD; + } + + m.step().expect("execution step failed"); + m.step().expect("execution step failed"); + + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), U256::zero()); + }; + } + + #[test] + fn test_mload_word() { + for sh in 0..32 { + evm_unit_test! { + (m) { + PUSH1; + {sh}; + MLOAD; + } + + m.state.memory.grow(32); + m.state.memory[..32].copy_from_slice(&U256::MAX.to_bytes()); + + m.step().expect("execution step failed"); + m.step().expect("execution step failed"); + + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), U256::MAX << (8 * sh)); + }; + } + } + + #[test] + fn test_mstore8_basic() { + for i in 0..=u8::MAX { + evm_unit_test! { + (m) { + PUSH1; + {i}; + PUSH0; + MSTORE8; + } + m.step().expect("execution step failed"); + m.step().expect("execution step failed"); + m.step().expect("execution step failed"); + + assert_eq!(m.state.stack.len(), 0); + assert_eq!(m.state.memory[0], i); + }; + } + } + + #[test] + fn test_mstore8_overwrite() { + evm_unit_test! { + (m) { + PUSH1; + 0x01; + PUSH1; + 0x01; + MSTORE8; + } + // index has garbage + m.state.memory.grow(32); + m.state.memory[0] = 0xab; + m.state.memory[1] = 0xff; + m.state.memory[2] = 0xfe; + + m.step().expect("execution step failed"); + m.step().expect("execution step failed"); + m.step().expect("execution step failed"); + + assert_eq!(m.state.stack.len(), 0); + // overwritten + assert_eq!(m.state.memory[1], 0x01); + // byte after isn't touched + assert_eq!(m.state.memory[2], 0xfe); + // byte before isn't touched + assert_eq!(m.state.memory[0], 0xab); + }; + } + + #[test] + fn test_mstore8_large_offset() { + for sh in 0..16 { + let i = 1u16 << sh; + let [a, b] = i.to_be_bytes(); + evm_unit_test! { + (m) { + PUSH1; + 0xff; + PUSH2; + {a}; + {b}; + MSTORE8; + } + m.step().expect("execution step failed"); + m.step().expect("execution step failed"); + m.step().expect("execution step failed"); + + assert_eq!(m.state.stack.len(), 0); + assert_eq!(m.state.memory[i as usize], 0xff); + // leading memory is zeroed + assert_eq!(&m.state.memory[..i as usize], &vec![0; i as usize]); + }; + } + } + + #[test] + fn test_mstore8_garbage() { + evm_unit_test! { + (m) { + PUSH32; + 0xff; + 0xff; + 0xff; + 0xff; + 0xff; + 0xff; + 0xff; + 0xff; + 0xff; + 0xff; + 0xff; + 0xff; + 0xff; + 0xff; + 0xff; + 0xff; + 0xff; + 0xff; + 0xff; + 0xff; + 0xff; + 0xff; + 0xff; + 0xff; + 0xff; + 0xff; + 0xff; + 0xff; + 0xff; + 0xff; + 0xff; + 0x01; + PUSH0; + MSTORE8; + } + m.step().expect("execution step failed"); + m.step().expect("execution step failed"); + m.step().expect("execution step failed"); + + assert_eq!(m.state.stack.len(), 0); + assert_eq!(m.state.memory[0], 0x01); + // garbage is not written alongside byte + assert_eq!(&m.state.memory[1..32], &[0; 31]); + }; + } + + #[test] + fn test_mstore_basic() { + evm_unit_test! { + (m) { + PUSH2; + 0xff; + 0xfe; + PUSH0; + MSTORE; + } + m.step().expect("execution step failed"); + m.step().expect("execution step failed"); + m.step().expect("execution step failed"); + + assert_eq!(m.state.stack.len(), 0); + assert_eq!(m.state.memory[30..32], [0xff, 0xfe]); + // nothing else is written to memory + assert_eq!(&m.state.memory[..30], &[0; 30]); + }; + } + + #[test] + fn test_mstore_overwrite() { + evm_unit_test! { + (m) { + PUSH2; + 0xff; + 0xfe; + PUSH0; + MSTORE; + } + m.state.memory.grow(64); + m.state.memory[..EVM_WORD_SIZE].copy_from_slice(&[0xff; EVM_WORD_SIZE]); + // single byte outside expected overwritten area + m.state.memory[32] = 0xfe; + + m.step().expect("execution step failed"); + m.step().expect("execution step failed"); + m.step().expect("execution step failed"); + + assert_eq!(m.state.stack.len(), 0); + assert_eq!(m.state.memory[30..32], [0xff, 0xfe]); + // zeroes fill remaining word + assert_eq!(&m.state.memory[..30], &[0; 30]); + // nothing written outside of word + assert_eq!(m.state.memory[32], 0xfe); + }; + } + + #[test] + fn test_msize_multiple_mstore8() { + evm_unit_test! { + (m) { + PUSH1; + 0xff; + PUSH1; + {42}; // offset of 42 + MSTORE8; + MSIZE; + } + + m.step().expect("execution step failed"); + m.step().expect("execution step failed"); + m.step().expect("execution step failed"); + m.step().expect("execution step failed"); + + assert_eq!(m.state.stack.len(), 1); + // msize is always a multiple of 32 + assert_eq!(m.state.stack.pop().unwrap(), U256::from(64)); + }; + } + + #[test] + fn test_msize_multiple_mstore() { + evm_unit_test! { + (m) { + PUSH1; + 0xff; + PUSH1; + {12}; // offset of 12 + MSTORE; + MSIZE; + } + + m.step().expect("execution step failed"); + m.step().expect("execution step failed"); + m.step().expect("execution step failed"); + m.step().expect("execution step failed"); + + assert_eq!(m.state.stack.len(), 1); + // 12 + 32 = 42, round up to nearest 32 = 64 + assert_eq!(m.state.stack.pop().unwrap(), U256::from(64)); + }; + } + + #[test] + fn test_msize_basic() { + // Demonstrate that MSIZE depends on memory.len() + // Normally this should never happen and we wont panic from it. + evm_unit_test! { + (m) { + MSIZE; + } + + m.state.memory.grow(12); + m.step().expect("execution step failed"); + assert_eq!(m.state.stack.pop().unwrap(), U256::from(32)); + }; + } + + macro_rules! check_mem { + ($mem:ident, $region:ident, $len:expr) => { + match $region { + Some(MemoryRegion { offset, size }) => { + let sizeu: usize = size.into(); + assert!(sizeu == $len); + assert!($mem.len() >= offset + sizeu); + for x in offset..offset + sizeu { + assert_eq!($mem[x], 0); + } + } + None => { + panic!("no memory region"); + } + } + }; + } + + #[test] + fn test_memread_simple() { + // simple read in bounds + let mut mem = Memory::default(); + mem.grow(1024); + assert_eq!(mem.len(), 1024); + + let region = get_memory_region(&mut mem, 0, 512).expect("memory read failed"); + check_mem!(mem, region, 512); + } + + #[test] + fn test_memread_simple2() { + // simple read in bounds + let mut mem = Memory::default(); + mem.grow(1024); + assert_eq!(mem.len(), 1024); + + let region = get_memory_region(&mut mem, 128, 512).expect("memory read failed"); + check_mem!(mem, region, 512); + } + + #[test] + fn test_memread_simple3() { + // simple read in bounds + let mut mem = Memory::default(); + mem.grow(1024); + assert_eq!(mem.len(), 1024); + + let region = get_memory_region(&mut mem, 512, 512).expect("memory read failed"); + check_mem!(mem, region, 512); + } + + #[test] + fn test_memread_empty() { + let mut mem = Memory::default(); + mem.grow(1024); + assert_eq!(mem.len(), 1024); + + let region = get_memory_region(&mut mem, 512, 0).expect("memory read failed"); + assert!(region.is_none()); + } + + #[test] + fn test_memread_overflow1() { + // len > mem size + let mut mem = Memory::default(); + mem.grow(1024); + assert_eq!(mem.len(), 1024); + + let region = get_memory_region(&mut mem, 0, 2048).expect("memory read failed"); + check_mem!(mem, region, 2048); + } + + #[test] + fn test_memread_overflow2() { + // offset > mem size + let mut mem = Memory::default(); + mem.grow(1024); + assert_eq!(mem.len(), 1024); + + let region = get_memory_region(&mut mem, 1056, 1024).expect("memory read failed"); + check_mem!(mem, region, 1024); + } + + #[test] + fn test_memread_overflow3() { + // offset+len > mem size + let mut mem = Memory::default(); + mem.grow(1024); + assert_eq!(mem.len(), 1024); + + let region = get_memory_region(&mut mem, 988, 2048).expect("memory read failed"); + check_mem!(mem, region, 2048); + } + + #[test] + fn test_memread_overflow_err() { + let mut mem = Memory::default(); + + let result = get_memory_region(&mut mem, u32::MAX - 1, 10); + assert!(result.is_err()); + assert_eq!(result.err().unwrap().exit_code(), crate::EVM_CONTRACT_ILLEGAL_MEMORY_ACCESS); + } +} diff --git a/actors/evm/src/interpreter/instructions/mod.rs b/actors/evm/src/interpreter/instructions/mod.rs new file mode 100644 index 000000000..7c9d7fe2d --- /dev/null +++ b/actors/evm/src/interpreter/instructions/mod.rs @@ -0,0 +1,363 @@ +#![allow(clippy::unnecessary_mut_passed)] + +mod arithmetic; +mod bitwise; +mod boolean; +mod call; +mod context; +mod control; +mod ext; +mod hash; +mod lifecycle; +mod log_event; +mod memory; +mod stack; +mod state; +mod storage; + +use crate::interpreter::execution::Machine; +use fil_actors_evm_shared::uints::U256; +use fil_actors_runtime::runtime::Runtime; +use fil_actors_runtime::ActorError; + +macro_rules! rev { + ($($args:ident),*) => { + rev!(() $($args),*) + }; + (($($reversed:ident),*) $first:ident $(, $rest:ident)*) => { + rev!(($first $(,$reversed)*) $($rest),*) + }; + (($($reversed:ident),*)) => { + [$($reversed),*] + }; +} + +macro_rules! def_op { + ($op:ident ($m:ident) => { $($body:tt)* }) => { + #[allow(non_snake_case)] + #[inline(always)] + pub fn $op<'r, 'a, RT: Runtime + 'a>($m: &mut Machine<'r, 'a, RT> ) -> Result<(), ActorError> { + $($body)* + } + } +} + +// macros for the instruction zoo: +// primops: take values of the stack and return a result value to be pushed on the stack +macro_rules! def_primop { + ($op:ident ($($arg:ident),+) => $impl:path) => { + def_op!{ $op (m) => { + let &rev![$($arg),*] = m.state.stack.pop_many()?; + let result = $impl($($arg),*); + m.state.stack.push_unchecked(result); + m.pc += 1; + Ok(()) + + }} + }; + ($op:ident () => $impl:path) => { + def_op!{ $op (m) => { + m.state.stack.ensure_one(); + let result = $impl($($arg),*); + m.state.stack.push_unchecked(result); + m.pc += 1; + Ok(()) + }} + }; +} + +// stackops: operate directly on the stack +macro_rules! def_stackop { + ($op:ident => $impl:path) => { + def_op! { $op (m) => { + $impl(&mut m.state.stack)?; + m.pc += 1; + Ok(()) + }} + }; +} + +// push variants: push stuff on the stack taken as input from bytecode; the kind of thing that +// makes you want to cry because it really is a stack op. +// Takes subslice of bytecode starting at pc. Advances pc by number of bytes read. +macro_rules! def_push { + ($op:ident => $impl:path) => { + def_op! { $op (m) => { + m.pc += 1; + let code = &m.bytecode[m.pc..]; + m.pc += $impl(&mut m.state.stack, code)?; + Ok(()) + }} + }; +} + +// stdfuns: take state and system as first args, and args from the stack and return a result value +// to be pushed in the stack. +macro_rules! def_stdfun { + ($op:ident ($($arg:ident),+) => $impl:path) => { + def_op!{ $op (m) => { + let &rev![$($arg),*] = m.state.stack.pop_many()?; + let result = $impl(&mut m.state, &mut m.system, $($arg),*)?; + m.state.stack.push_unchecked(result); + m.pc += 1; + Ok(()) + }} + }; + ($op:ident () => $impl:path) => { + def_op!{ $op (m) => { + m.state.stack.ensure_one()?; + let result = $impl(&mut m.state, &mut m.system)?; + m.state.stack.push_unchecked(result); + m.pc += 1; + Ok(()) + }} + }; +} + +// stdproc: like stdfun, but returns no value +macro_rules! def_stdproc { + ($op:ident ($($arg:ident),*) => $impl:path) => { + def_op!{ $op (m) => { + let &rev![$($arg),*] = m.state.stack.pop_many()?; + $impl(&mut m.state, &mut m.system, $($arg),*)?; + m.pc += 1; + Ok(()) + }} + } +} + +// std*_code: code reflective functionoid +macro_rules! def_stdfun_code { + ($op:ident ($($arg:ident),+) => $impl:path) => { + def_op!{ $op (m) => { + let &rev![$($arg),*] = m.state.stack.pop_many()?; + let result = $impl(&mut m.state, &mut m.system, m.bytecode.as_ref(), $($arg),*)?; + m.state.stack.push_unchecked(result); + m.pc += 1; + Ok(()) + }} + }; + ($op:ident () => $impl:path) => { + def_op!{ $op (m) => { + m.state.stack.ensure_one()?; + let result = $impl(&mut m.state, &mut m.system, m.bytecode.as_ref())?; + m.state.stack.push_unchecked(result); + m.pc += 1; + Ok(()) + }} + }; +} + +// and the procedural variant +macro_rules! def_stdproc_code { + ($op:ident ($($arg:ident),*) => $impl:path) => { + def_op!{ $op (m) => { + let &rev![$($arg),*] = m.state.stack.pop_many()?; + $impl(&mut m.state, &mut m.system, m.bytecode.as_ref(), $($arg),*)?; + m.pc += 1; + Ok(()) + }} + } +} + +// stdproc: logging functionoid +macro_rules! def_stdlog { + ($op:ident ($ntopics:literal, ($($topic:ident),*))) => { + def_op!{ $op (m) => { + let &rev![a, b $(,$topic)*] = m.state.stack.pop_many()?; + log_event::log(&mut m.state, &mut m.system, $ntopics, a, b, &[$($topic),*])?; + m.pc += 1; + Ok(()) + }} + } +} + +// jmp: jump variants +macro_rules! def_jmp { + ($op:ident ($($arg:ident),*) => $impl:path) => { + def_op!{ $op (m) => { + let &rev![$($arg),*] = m.state.stack.pop_many()?; + m.pc = $impl(m.bytecode, m.pc, $($arg),*)?; + Ok(()) + }} + } + +} +macro_rules! def_exit { + ($op:ident ($($arg:ident),*) => $impl:path) => { + def_op!{ $op (m) => { + let &rev![$($arg),*] = m.state.stack.pop_many()?; + m.output = $impl(&mut m.state, &mut m.system, $($arg),*)?; + m.pc = m.bytecode.len(); // stop execution + Ok(()) + }} + } +} + +// special: pc and things like that +macro_rules! def_special { + ($op:ident ($m:ident) => $value:expr) => { + def_op! { $op (m) => { + let result = { + let $m = &mut *m; + $value + }; + m.state.stack.push(result)?; + m.pc += 1; + Ok(()) + }} + }; +} + +// IMPLEMENTATION +// arithmetic +def_primop! { ADD(a, b) => arithmetic::add } +def_primop! { MUL(a, b) => arithmetic::mul } +def_primop! { SUB(a, b) => arithmetic::sub } +def_primop! { DIV(a, b) => arithmetic::div } +def_primop! { SDIV(a, b) => arithmetic::sdiv } +def_primop! { MOD(a, b) => arithmetic::modulo } +def_primop! { SMOD(a, b) => arithmetic::smod } +def_primop! { ADDMOD(a, b, c) => arithmetic::addmod } +def_primop! { MULMOD(a, b, c) => arithmetic::mulmod } +def_primop! { EXP(a, b) => arithmetic::exp } +def_primop! { SIGNEXTEND(a, b) => arithmetic::signextend } +// boolean +def_primop! { LT(a, b) => boolean::lt } +def_primop! { GT(a, b) => boolean::gt } +def_primop! { SLT(a, b) => boolean::slt } +def_primop! { SGT(a, b) => boolean::sgt } +def_primop! { EQ(a, b) => boolean::eq } +def_primop! { ISZERO(a) => boolean::iszero } +def_primop! { AND(a, b) => boolean::and } +def_primop! { OR(a, b) => boolean::or } +def_primop! { XOR(a, b) => boolean::xor } +def_primop! { NOT(a) => boolean::not } +// bitwise +def_primop! { BYTE(a, b) => bitwise::byte } +def_primop! { SHL(a, b) => bitwise::shl } +def_primop! { SHR(a, b) => bitwise::shr } +def_primop! { SAR(a, b) => bitwise::sar } +// dup +def_stackop! { DUP1 => stack::dup::<1> } +def_stackop! { DUP2 => stack::dup::<2> } +def_stackop! { DUP3 => stack::dup::<3> } +def_stackop! { DUP4 => stack::dup::<4> } +def_stackop! { DUP5 => stack::dup::<5> } +def_stackop! { DUP6 => stack::dup::<6> } +def_stackop! { DUP7 => stack::dup::<7> } +def_stackop! { DUP8 => stack::dup::<8> } +def_stackop! { DUP9 => stack::dup::<9> } +def_stackop! { DUP10 => stack::dup::<10> } +def_stackop! { DUP11 => stack::dup::<11> } +def_stackop! { DUP12 => stack::dup::<12> } +def_stackop! { DUP13 => stack::dup::<13> } +def_stackop! { DUP14 => stack::dup::<14> } +def_stackop! { DUP15 => stack::dup::<15> } +def_stackop! { DUP16 => stack::dup::<16> } +// swap +def_stackop! { SWAP1 => stack::swap::<1> } +def_stackop! { SWAP2 => stack::swap::<2> } +def_stackop! { SWAP3 => stack::swap::<3> } +def_stackop! { SWAP4 => stack::swap::<4> } +def_stackop! { SWAP5 => stack::swap::<5> } +def_stackop! { SWAP6 => stack::swap::<6> } +def_stackop! { SWAP7 => stack::swap::<7> } +def_stackop! { SWAP8 => stack::swap::<8> } +def_stackop! { SWAP9 => stack::swap::<9> } +def_stackop! { SWAP10 => stack::swap::<10> } +def_stackop! { SWAP11 => stack::swap::<11> } +def_stackop! { SWAP12 => stack::swap::<12> } +def_stackop! { SWAP13 => stack::swap::<13> } +def_stackop! { SWAP14 => stack::swap::<14> } +def_stackop! { SWAP15 => stack::swap::<15> } +def_stackop! { SWAP16 => stack::swap::<16> } +// pop +def_stackop! { POP => stack::pop } +// push +def_push! { PUSH0 => stack::push::<0> } +def_push! { PUSH1 => stack::push::<1> } +def_push! { PUSH2 => stack::push::<2> } +def_push! { PUSH3 => stack::push::<3> } +def_push! { PUSH4 => stack::push::<4> } +def_push! { PUSH5 => stack::push::<5> } +def_push! { PUSH6 => stack::push::<6> } +def_push! { PUSH7 => stack::push::<7> } +def_push! { PUSH8 => stack::push::<8> } +def_push! { PUSH9 => stack::push::<9> } +def_push! { PUSH10 => stack::push::<10> } +def_push! { PUSH11 => stack::push::<11> } +def_push! { PUSH12 => stack::push::<12> } +def_push! { PUSH13 => stack::push::<13> } +def_push! { PUSH14 => stack::push::<14> } +def_push! { PUSH15 => stack::push::<15> } +def_push! { PUSH16 => stack::push::<16> } +def_push! { PUSH17 => stack::push::<17> } +def_push! { PUSH18 => stack::push::<18> } +def_push! { PUSH19 => stack::push::<19> } +def_push! { PUSH20 => stack::push::<20> } +def_push! { PUSH21 => stack::push::<21> } +def_push! { PUSH22 => stack::push::<22> } +def_push! { PUSH23 => stack::push::<23> } +def_push! { PUSH24 => stack::push::<24> } +def_push! { PUSH25 => stack::push::<25> } +def_push! { PUSH26 => stack::push::<26> } +def_push! { PUSH27 => stack::push::<27> } +def_push! { PUSH28 => stack::push::<28> } +def_push! { PUSH29 => stack::push::<29> } +def_push! { PUSH30 => stack::push::<30> } +def_push! { PUSH31 => stack::push::<31> } +def_push! { PUSH32 => stack::push::<32> } +// functionoids +def_stdfun! { KECCAK256(a, b) => hash::keccak256 } +def_stdfun! { ADDRESS() => context::address } +def_stdfun! { BALANCE(a) => state::balance } +def_stdfun! { ORIGIN() => context::origin } +def_stdfun! { CALLER() => context::caller } +def_stdfun! { CALLVALUE() => context::call_value } +def_stdfun! { CALLDATALOAD(a) => call::calldataload } +def_stdfun! { CALLDATASIZE() => call::calldatasize } +def_stdproc! { CALLDATACOPY(a, b, c) => call::calldatacopy } +def_stdfun! { GASPRICE() => context::gas_price } +def_stdfun! { EXTCODESIZE(a) => ext::extcodesize } +def_stdproc! { EXTCODECOPY(a, b, c, d) => ext::extcodecopy } +def_stdfun! { EXTCODEHASH(a) => ext::extcodehash } +def_stdfun! { RETURNDATASIZE() => control::returndatasize } +def_stdproc! { RETURNDATACOPY(a, b, c) => control::returndatacopy } +def_stdfun! { BLOCKHASH(a) => context::blockhash } +def_stdfun! { COINBASE() => context::coinbase } +def_stdfun! { TIMESTAMP() => context::timestamp } +def_stdfun! { NUMBER() => context::block_number } +def_stdfun! { PREVRANDAO() => context::prevrandao } +def_stdfun! { GASLIMIT() => context::gas_limit } +def_stdfun! { CHAINID() => context::chain_id } +def_stdfun! { BASEFEE() => context::base_fee } +def_stdfun! { SELFBALANCE() => state::selfbalance } +def_stdfun! { MLOAD(a) => memory::mload } +def_stdproc! { MSTORE(a, b) => memory::mstore } +def_stdproc! { MSTORE8(a, b) => memory::mstore8 } +def_stdfun! { SLOAD(a) => storage::sload } +def_stdproc! { SSTORE(a, b) => storage::sstore } +def_stdfun! { MSIZE() => memory::msize } +def_stdfun! { GAS() => context::gas } +def_stdlog! { LOG0(0, ()) } +def_stdlog! { LOG1(1, (topic1)) } +def_stdlog! { LOG2(2, (topic1, topic2)) } +def_stdlog! { LOG3(3, (topic1, topic2, topic3)) } +def_stdlog! { LOG4(4, (topic1, topic2, topic3, topic4)) } +def_stdfun! { CALL(gas, dst, value, ioff, isz, ooff, osz) => call::call_call } +def_stdfun! { DELEGATECALL(gas, dst, ioff, isz, ooff, osz) => call::call_delegatecall } +def_stdfun! { STATICCALL(gas, dst, ioff, isz, ooff, osz) => call::call_staticcall } +def_stdfun_code! { CODESIZE() => call::codesize } +def_stdproc_code! { CODECOPY(a, b, c) => call::codecopy } +def_stdfun! { CREATE(a, b, c) => lifecycle::create } +def_stdfun! { CREATE2(a, b, c, d) => lifecycle::create2 } +def_stdproc! { JUMPDEST() => control::nop } +def_stdproc! { INVALID() => control::invalid } +def_exit! { RETURN(a, b) => control::ret } +def_exit! { REVERT(a, b) => control::revert } +def_exit! { STOP() => control::stop } +def_exit! { SELFDESTRUCT(a) => lifecycle::selfdestruct } +def_jmp! { JUMP(a) => control::jump } +def_jmp! { JUMPI(a, b) => control::jumpi } +def_special! { PC(m) => U256::from(m.pc) } diff --git a/actors/evm/src/interpreter/instructions/stack.rs b/actors/evm/src/interpreter/instructions/stack.rs new file mode 100644 index 000000000..67d7d6d20 --- /dev/null +++ b/actors/evm/src/interpreter/instructions/stack.rs @@ -0,0 +1,121 @@ +#![allow(clippy::missing_safety_doc)] + +use fil_actors_evm_shared::uints::U256; +use fil_actors_runtime::ActorError; + +use crate::interpreter::stack::Stack; + +macro_rules! be_u64 { + ($byte:expr) => {$byte as u64}; + ($byte1:expr $(,$rest:expr)*) => { + (($byte1 as u64) << (be_shift!{$($rest),*})) + be_u64!{$($rest),*} + }; +} + +macro_rules! be_shift { + ($byte:expr) => {8}; + ($byte:expr $(,$rest:expr)*) => {8 + be_shift!{$($rest),*}}; +} + +#[inline] +/// Pushes code[..LEN] right padded bytes as a single word to the stack. +/// +/// Returns error on stack overflow. +/// +/// Panics if `LEN` > 32. +pub(crate) fn push(stack: &mut Stack, code: &[u8]) -> Result { + if code.len() < LEN { + // this is a pathological edge case, as the contract will immediately stop execution. + // we still handle it for correctness sake (and obviously avoid crashing out of bounds) + let mut padded = [0; LEN]; + padded[..code.len()].copy_from_slice(code); + stack.push(U256::from_big_endian(&padded))?; + } else { + stack.push(match LEN { + // explicitly unroll up to u64 (single limb) pushes + 0 => U256::ZERO, + 1 => U256::from_u64(be_u64! {code[0]}), + 2 => U256::from_u64(be_u64! {code[0], code[1]}), + 3 => U256::from_u64(be_u64! {code[0], code[1], code[2]}), + 4 => U256::from_u64(be_u64! {code[0], code[1], code[2], code[3]}), + 5 => U256::from_u64(be_u64! {code[0], code[1], code[2], code[3], code[4]}), + 6 => U256::from_u64(be_u64! {code[0], code[1], code[2], code[3], code[4], code[5]}), + 7 => U256::from_u64( + be_u64! {code[0], code[1], code[2], code[3], code[4], code[5], code[6]}, + ), + 8 => U256::from_u64( + be_u64! {code[0], code[1], code[2], code[3], code[4], code[5], code[6], code[7]}, + ), + _ => U256::from_big_endian(&code[..LEN]), + })?; + } + Ok(LEN) +} + +#[inline] +pub(crate) fn dup(stack: &mut Stack) -> Result<(), ActorError> { + stack.dup(HEIGHT) +} + +#[inline] +pub(crate) fn swap(stack: &mut Stack) -> Result<(), ActorError> { + stack.swap_top(HEIGHT) +} + +#[inline] +pub(crate) fn pop(stack: &mut Stack) -> Result<(), ActorError> { + stack.drop() +} + +#[test] +fn test_push_pad_right() { + let mut stack = Stack::new(); + assert_eq!(push::<4>(&mut stack, &[0xde, 0xad]).unwrap(), 4); + assert_eq!(stack.len(), 1); + assert_eq!(stack.pop().unwrap(), U256::from(0xdead0000u64)); +} + +#[test] +fn test_push0() { + let mut stack = Stack::new(); + assert_eq!(push::<0>(&mut stack, &[0x1]).unwrap(), 0); + assert_eq!(stack.len(), 1); + assert_eq!(stack.pop().unwrap(), U256::ZERO); + + assert_eq!(push::<0>(&mut stack, &[0xff; 100]).unwrap(), 0); + assert_eq!(stack.len(), 1); + assert_eq!(stack.pop().unwrap(), U256::ZERO); +} + +#[test] +fn test_push_n() { + let mut stack = Stack::new(); + assert_eq!(push::<1>(&mut stack, &[0x1]).unwrap(), 1); + assert_eq!(stack.len(), 1); + assert_eq!(stack.pop().unwrap(), U256::ONE); + + assert_eq!(push::<32>(&mut stack, &[0xff; 100]).unwrap(), 32); + assert_eq!(stack.len(), 1); + assert_eq!(stack.pop().unwrap(), U256::MAX); +} +#[test] +#[should_panic] +fn test_push_33() { + let mut stack = Stack::new(); + push::<33>(&mut stack, &[0xff; 100]).unwrap(); +} + +#[test] +fn test_dup_n() { + let mut stack = Stack::new(); + + stack.push(U256::from(0xff)).unwrap(); + dup::<1>(&mut stack).unwrap(); + assert_eq!(stack.pop(), stack.pop()); + + stack.push(U256::from(0xff)).unwrap(); + dup::<2>(&mut stack).expect_err("stack underflow"); + + stack.push(U256::from(0)).unwrap(); + dup::<2>(&mut stack).unwrap(); +} diff --git a/actors/evm/src/interpreter/instructions/state.rs b/actors/evm/src/interpreter/instructions/state.rs new file mode 100644 index 000000000..7571ad345 --- /dev/null +++ b/actors/evm/src/interpreter/instructions/state.rs @@ -0,0 +1,142 @@ +use fil_actors_evm_shared::{address::EthAddress, uints::U256}; +use fil_actors_runtime::ActorError; +use fvm_shared::address::Address; + +use { + crate::interpreter::{ExecutionState, System}, + fil_actors_runtime::runtime::Runtime, +}; + +#[inline] +pub fn balance( + _state: &mut ExecutionState, + system: &System, + actor: U256, +) -> Result { + let addr: EthAddress = actor.into(); + let addr: Address = addr.into(); + + let balance = system + .rt + .resolve_address(&addr) + .and_then(|id| system.rt.actor_balance(id).as_ref().map(U256::from)) + .unwrap_or_default(); + + Ok(balance) +} + +#[inline] +pub fn selfbalance( + _state: &mut ExecutionState, + system: &System, +) -> Result { + // Returns native FIL balance of the receiver. Value precision is identical to Ethereum, so + // no conversion needed (atto, 1e18). + Ok(U256::from(&system.rt.current_balance())) +} + +#[cfg(test)] +mod test { + use fil_actors_evm_shared::{address::EthAddress, uints::U256}; + use fvm_shared::address::Address; + + use crate::evm_unit_test; + + #[test] + fn balance_basic() { + for balance in [0, 1234, u64::MAX as u128, u128::MAX] { + for (has_id, addr) in [ + (true, EthAddress::from_id(1111).as_evm_word()), // eth encoded IDs are OK + (true, EthAddress([0xff; 20]).as_evm_word()), + (true, EthAddress([0xab; 20]).as_evm_word()), + (false, EthAddress([0xff; 20]).as_evm_word()), + (false, EthAddress([0xab; 20]).as_evm_word()), + (false, U256::MAX), + ] { + evm_unit_test! { + (rt) { + let id_address = 1111; + if has_id { + let addr: EthAddress = addr.into(); + rt.add_id_address(addr.into(), Address::new_id(id_address)) + } + rt.actor_balances.insert(id_address, TokenAmount::from_atto(balance)); + } + (m) { + PUSH0; + MLOAD; + BALANCE; + } + + m.state.memory.grow(32); + m.state.memory[..32].copy_from_slice(&addr.to_bytes()); + + m.step().expect("execution step failed"); + m.step().expect("execution step failed"); + m.step().expect("execution step failed"); + + assert_eq!(m.state.stack.len(), 1); + if has_id { + assert_eq!(m.state.stack.pop().unwrap(), U256::from(balance)); + } else { + assert_eq!(m.state.stack.pop().unwrap(), U256::from(0)); + } + }; + } + } + } + + #[test] + fn balance_invalid_input() { + let id = 0xad; + let balance = 1234; + + let mut buf = EthAddress::from_id(id).as_evm_word().to_bytes(); + // first bytes should be ignored silently + buf[..12].copy_from_slice(&[0xff; 12]); + let addr = U256::from(buf); + + evm_unit_test! { + (rt) { + // 0xff id address gets balance + rt.actor_balances.insert(id as u64, TokenAmount::from_atto(balance)); + } + (m) { + PUSH0; + MLOAD; + BALANCE; + } + + m.state.memory.grow(32); + m.state.memory[..32].copy_from_slice(&addr.to_bytes()); + + m.step().expect("execution step failed"); + m.step().expect("execution step failed"); + m.step().expect("execution step failed"); + + assert_eq!(m.state.stack.len(), 1); + // balance should be set properly + assert_eq!(m.state.stack.pop().unwrap(), U256::from(balance)); + }; + } + + #[test] + fn selfbalance_basic() { + for i in 0..256 { + let balance = U256::ONE << i; + evm_unit_test! { + (rt) { + rt.add_balance(TokenAmount::from(&balance)); + } + (m) { + SELFBALANCE; + } + + m.step().expect("execution step failed"); + + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), balance); + }; + } + } +} diff --git a/actors/evm/src/interpreter/instructions/storage.rs b/actors/evm/src/interpreter/instructions/storage.rs new file mode 100644 index 000000000..06e66cc8a --- /dev/null +++ b/actors/evm/src/interpreter/instructions/storage.rs @@ -0,0 +1,85 @@ +use fil_actors_evm_shared::uints::U256; +use fil_actors_runtime::ActorError; + +use { + crate::interpreter::{ExecutionState, System}, + fil_actors_runtime::runtime::Runtime, +}; + +#[inline] +pub fn sload( + _state: &mut ExecutionState, + system: &mut System, + location: U256, +) -> Result { + // get from storage and place on stack + system.get_storage(location) +} + +#[inline] +pub fn sstore( + _state: &mut ExecutionState, + system: &mut System, + key: U256, + value: U256, +) -> Result<(), ActorError> { + if system.readonly { + return Err(ActorError::read_only("store called while read-only".into())); + } + + system.set_storage(key, value) +} + +#[cfg(test)] +mod tests { + use fil_actors_evm_shared::uints::U256; + + use crate::evm_unit_test; + + #[test] + fn test_sload() { + // happy path + evm_unit_test! { + (m) { + SLOAD; + } + m.system.set_storage(U256::from(0), U256::from(0x42)).unwrap(); + m.state.stack.push(U256::from(0)).unwrap(); + let result = m.step(); + assert!(result.is_ok(), "execution step failed"); + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), U256::from(0x42)); + }; + } + + #[test] + fn test_sload_oob() { + // oob access -- it is a zero + evm_unit_test! { + (m) { + SLOAD; + } + m.state.stack.push(U256::from(1234)).unwrap(); + let result = m.step(); + assert!(result.is_ok(), "execution step failed"); + assert_eq!(m.state.stack.len(), 1); + assert_eq!(m.state.stack.pop().unwrap(), U256::from(0)); + }; + } + + #[test] + fn test_sstore() { + evm_unit_test! { + (m) { + SSTORE; + } + + m.state.stack.push(U256::from(0x42)).unwrap(); + m.state.stack.push(U256::from(0)).unwrap(); + let result = m.step(); + assert!(result.is_ok(), "execution step failed"); + assert_eq!(m.state.stack.len(), 0); + assert_eq!(m.system.get_storage(U256::from(0)).unwrap(), U256::from(0x42)); + }; + } +} diff --git a/actors/evm/src/interpreter/memory.rs b/actors/evm/src/interpreter/memory.rs new file mode 100644 index 000000000..95185ba37 --- /dev/null +++ b/actors/evm/src/interpreter/memory.rs @@ -0,0 +1,77 @@ +use crate::EVM_WORD_SIZE; +use std::ops::{Deref, DerefMut}; + +const PAGE_SIZE: usize = 4 * 1024; + +#[derive(Clone, Debug)] +pub struct Memory(Vec); + +impl Deref for Memory { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + &*self.0 + } +} + +impl DerefMut for Memory { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut *self.0 + } +} + +impl Default for Memory { + fn default() -> Self { + Self(Vec::with_capacity(PAGE_SIZE)) + } +} + +impl Memory { + #[inline] + /// Reserve extra pages of memory + fn reserve_pages(&mut self, pages: usize) { + self.0.reserve((PAGE_SIZE * pages) - self.0.len()); + } + + #[inline] + /// Grows memory to a new size, reserving extra pages as-needed. + /// `new_size` may be unaligned. + /// + /// Do nothing if `new_size` doesn't grow memory. + pub fn grow(&mut self, mut new_size: usize) { + if new_size <= self.len() { + return; + } + + // Align to the next u256. + // Guaranteed to not overflow. + let alignment = new_size % EVM_WORD_SIZE; + if alignment > 0 { + new_size += EVM_WORD_SIZE - alignment; + } + + // Reserve any new pages. + let cap = self.0.capacity(); + if new_size > cap { + let required_pages = (new_size + PAGE_SIZE - 1) / PAGE_SIZE; + self.reserve_pages(required_pages); + } + + debug_assert_eq!(new_size % 32, 0, "MSIZE depends that memory is aligned to 32 bytes"); + // Grow to new aligned size. + self.0.resize(new_size, 0); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn grow() { + let mut mem = Memory::default(); + mem.grow(PAGE_SIZE * 2 + 1); + assert_eq!(mem.len(), PAGE_SIZE * 2 + EVM_WORD_SIZE); + assert_eq!(mem.0.capacity(), PAGE_SIZE * 3); + } +} diff --git a/actors/evm/src/interpreter/mod.rs b/actors/evm/src/interpreter/mod.rs new file mode 100644 index 000000000..a79fca0e5 --- /dev/null +++ b/actors/evm/src/interpreter/mod.rs @@ -0,0 +1,26 @@ +mod bytecode; +mod execution; +mod instructions; +mod memory; +mod output; +mod precompiles; +mod stack; +pub mod system; + +#[cfg(test)] +pub mod test_util; + +pub use { + bytecode::Bytecode, + execution::{execute, opcodes, ExecutionState}, + output::{Outcome, Output}, + system::System, +}; + +/// The kind of call-like instruction. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum CallKind { + Call, + DelegateCall, + StaticCall, +} diff --git a/actors/evm/src/interpreter/output.rs b/actors/evm/src/interpreter/output.rs new file mode 100644 index 000000000..b9a1ae078 --- /dev/null +++ b/actors/evm/src/interpreter/output.rs @@ -0,0 +1,17 @@ +use std::fmt::Debug; + +#[derive(Debug, Clone, PartialEq, Eq, Default)] +pub enum Outcome { + #[default] + Return, + Revert, +} + +/// Output of EVM execution. +#[derive(Debug, Clone, PartialEq, Eq, Default)] +pub struct Output { + /// Indicates the "outcome" of the execution. + pub outcome: Outcome, + /// The return data. + pub return_data: Vec, +} diff --git a/actors/evm/src/interpreter/precompiles/blake2f_impl.rs b/actors/evm/src/interpreter/precompiles/blake2f_impl.rs new file mode 100644 index 000000000..c21ca8606 --- /dev/null +++ b/actors/evm/src/interpreter/precompiles/blake2f_impl.rs @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: Apache-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2020-2022 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// The precomputed values for BLAKE2b [from the +/// spec](https://tools.ietf.org/html/rfc7693#section-2.7) There are 10 16-byte arrays - one for +/// each round the entries are calculated from the sigma constants. +const SIGMA: [[usize; 16]; 10] = [ + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3], + [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4], + [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8], + [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13], + [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9], + [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11], + [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10], + [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5], + [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0], +]; + +/// IV is the initialization vector for BLAKE2b. See https://tools.ietf.org/html/rfc7693#section-2.6 +/// for details. +const IV: [u64; 8] = [ + 0x6a09e667f3bcc908, + 0xbb67ae8584caa73b, + 0x3c6ef372fe94f82b, + 0xa54ff53a5f1d36f1, + 0x510e527fade682d1, + 0x9b05688c2b3e6c1f, + 0x1f83d9abfb41bd6b, + 0x5be0cd19137e2179, +]; + +#[inline(always)] +/// The G mixing function. See https://tools.ietf.org/html/rfc7693#section-3.1 +fn g(v: &mut [u64], a: usize, b: usize, c: usize, d: usize, x: u64, y: u64) { + v[a] = v[a].wrapping_add(v[b]).wrapping_add(x); + v[d] = (v[d] ^ v[a]).rotate_right(32); + v[c] = v[c].wrapping_add(v[d]); + v[b] = (v[b] ^ v[c]).rotate_right(24); + v[a] = v[a].wrapping_add(v[b]).wrapping_add(y); + v[d] = (v[d] ^ v[a]).rotate_right(16); + v[c] = v[c].wrapping_add(v[d]); + v[b] = (v[b] ^ v[c]).rotate_right(63); +} + +/// The Blake2 compression function F. See https://tools.ietf.org/html/rfc7693#section-3.2 +/// Takes as an argument the state vector `h`, message block vector `m`, offset counter `t`, final +/// block indicator flag `f`, and number of rounds `rounds`. The state vector provided as the first +/// parameter is modified by the function. +pub fn compress(h: &mut [u64; 8], m: [u64; 16], t: [u64; 2], f: bool, rounds: usize) { + let mut v = [0u64; 16]; + v[..h.len()].copy_from_slice(h); // First half from state. + v[h.len()..].copy_from_slice(&IV); // Second half from IV. + + v[12] ^= t[0]; + v[13] ^= t[1]; + + if f { + v[14] = !v[14] // Invert all bits if the last-block-flag is set. + } + for i in 0..rounds { + // Message word selection permutation for this round. + let s = &SIGMA[i % 10]; + g(&mut v, 0, 4, 8, 12, m[s[0]], m[s[1]]); + g(&mut v, 1, 5, 9, 13, m[s[2]], m[s[3]]); + g(&mut v, 2, 6, 10, 14, m[s[4]], m[s[5]]); + g(&mut v, 3, 7, 11, 15, m[s[6]], m[s[7]]); + + g(&mut v, 0, 5, 10, 15, m[s[8]], m[s[9]]); + g(&mut v, 1, 6, 11, 12, m[s[10]], m[s[11]]); + g(&mut v, 2, 7, 8, 13, m[s[12]], m[s[13]]); + g(&mut v, 3, 4, 9, 14, m[s[14]], m[s[15]]); + } + + for i in 0..8 { + h[i] ^= v[i] ^ v[i + 8]; + } +} diff --git a/actors/evm/src/interpreter/precompiles/evm.rs b/actors/evm/src/interpreter/precompiles/evm.rs new file mode 100644 index 000000000..e47f6979c --- /dev/null +++ b/actors/evm/src/interpreter/precompiles/evm.rs @@ -0,0 +1,853 @@ +use std::ops::Range; + +use fil_actors_evm_shared::uints::{ + byteorder::{ByteOrder, LE}, + U256, +}; +use fil_actors_runtime::runtime::Runtime; +use fvm_shared::crypto::{ + hash::SupportedHashes, + signature::{SECP_SIG_LEN, SECP_SIG_MESSAGE_HASH_SIZE}, +}; +use num_traits::{One, Zero}; +use substrate_bn::{pairing_batch, AffineG1, AffineG2, Fq, Fq2, Fr, Group, Gt, G1, G2}; + +use crate::{ + interpreter::{precompiles::PrecompileError, System}, + EVM_WORD_SIZE, +}; + +use super::{PrecompileContext, PrecompileResult}; +use crate::reader::ValueReader; + +const SECP256K1_N: U256 = + U256::from_u128_words(0xfffffffffffffffffffffffffffffffe, 0xbaaedce6af48a03bbfd25e8cd0364141); + +const SECP256K1_RANGE: Range = U256::ONE..SECP256K1_N; + +#[test] +fn test_secp_range() { + assert!(SECP256K1_RANGE.contains(&U256::ONE)); + assert!(!SECP256K1_RANGE.contains(&U256::ZERO)); + assert!(!SECP256K1_RANGE.contains(&SECP256K1_N)); +} + +fn ec_recover_internal(system: &mut System, input: &[u8]) -> PrecompileResult { + let mut input_params = ValueReader::new(input); + let hash: [u8; SECP_SIG_MESSAGE_HASH_SIZE] = input_params.read_fixed(); + let recovery_byte: u8 = input_params.read_value()?; + let r: U256 = input_params.read_value()?; + let s: U256 = input_params.read_value()?; + + // Must be either 27 or 28 + let v = recovery_byte.checked_sub(27).ok_or(PrecompileError::InvalidInput)?; + + // SECP256K1_HALF_N check in evm was disabled after homestead, both r and s can be in full range of N + if v > 1 || !SECP256K1_RANGE.contains(&r) || !SECP256K1_RANGE.contains(&s) { + return Err(PrecompileError::InvalidInput); + } + + let mut sig: [u8; SECP_SIG_LEN] = [0u8; 65]; + r.to_big_endian(&mut sig[..32]); + s.to_big_endian(&mut sig[32..64]); + sig[64] = v; + + let pubkey = system + .rt + .recover_secp_public_key(&hash, &sig) + .map_err(|_| PrecompileError::InvalidInput)?; + + let mut address = system.rt.hash(SupportedHashes::Keccak256, &pubkey[1..]); + address[..12].copy_from_slice(&[0u8; 12]); + + Ok(address) +} + +/// recover a secp256k1 pubkey from a hash, recovery byte, and a signature +pub(super) fn ec_recover( + system: &mut System, + input: &[u8], + _: PrecompileContext, +) -> PrecompileResult { + // This precompile is weird and never fails. So we just turn errors into empty results. + Ok(ec_recover_internal(system, input).unwrap_or_default()) +} + +/// hash with sha2-256 +pub(super) fn sha256( + system: &mut System, + input: &[u8], + _: PrecompileContext, +) -> PrecompileResult { + Ok(system.rt.hash(SupportedHashes::Sha2_256, input)) +} + +/// hash with ripemd160 +pub(super) fn ripemd160( + system: &mut System, + input: &[u8], + _: PrecompileContext, +) -> PrecompileResult { + let mut out = vec![0; 12]; + let hash = system.rt.hash(SupportedHashes::Ripemd160, input); + out.extend_from_slice(&hash); + debug_assert_eq!(out.len(), EVM_WORD_SIZE); + Ok(out) +} + +/// data copy +pub(super) fn identity( + _: &mut System, + input: &[u8], + _: PrecompileContext, +) -> PrecompileResult { + Ok(Vec::from(input)) +} + +// https://eips.ethereum.org/EIPS/eip-198 +/// modulus exponent a number +pub(super) fn modexp( + _: &mut System, + input: &[u8], + _: PrecompileContext, +) -> PrecompileResult { + let mut reader = ValueReader::new(input); + + // This will error out if the user passes values greater than u32, but that's fine. The user + // would run out of gas anyways. + let base_len = reader.read_value::()? as usize; + let exponent_len = reader.read_value::()? as usize; + let mod_len = reader.read_value::()? as usize; + + if base_len == 0 && mod_len == 0 { + return Ok(Vec::new()); + } + + let base = reader.read_biguint(base_len); + let exponent = reader.read_biguint(exponent_len); + let modulus = reader.read_biguint(mod_len); + + if modulus.is_zero() || modulus.is_one() { + // mod 0 is undefined: 0, base mod 1 is always 0 + return Ok(vec![0; mod_len]); + } + + let mut output = base.modpow(&exponent, &modulus).to_bytes_be(); + + if output.len() < mod_len { + let mut ret = Vec::with_capacity(mod_len); + ret.resize(mod_len - output.len(), 0); // left padding + ret.extend_from_slice(&output); + output = ret; + } + + Ok(output) +} + +pub(super) fn curve_to_vec(curve: G1) -> Vec { + let mut output = vec![0; 64]; + if let Some(product) = AffineG1::from_jacobian(curve) { + product.x().to_big_endian(&mut output[0..32]).unwrap(); + product.y().to_big_endian(&mut output[32..64]).unwrap(); + } + output +} + +/// add 2 points together on an elliptic curve +pub(super) fn ec_add( + _: &mut System, + input: &[u8], + _: PrecompileContext, +) -> PrecompileResult { + let mut input_params = ValueReader::new(input); + let point1: G1 = input_params.read_value()?; + let point2: G1 = input_params.read_value()?; + + Ok(curve_to_vec(point1 + point2)) +} + +/// multiply a point on an elliptic curve by a scalar value +pub(super) fn ec_mul( + _: &mut System, + input: &[u8], + _: PrecompileContext, +) -> PrecompileResult { + let mut input_params = ValueReader::new(input); + let point: G1 = input_params.read_value()?; + let scalar: Fr = input_params.read_value()?; + + Ok(curve_to_vec(point * scalar)) +} + +/// pairs multple groups of twisted bn curves +pub(super) fn ec_pairing( + _: &mut System, + input: &[u8], + _: PrecompileContext, +) -> PrecompileResult { + fn read_group(input: &[u8]) -> Result<(G1, G2), PrecompileError> { + let mut reader = ValueReader::new(input); + + let x: Fq = reader.read_value()?; + let y: Fq = reader.read_value()?; + + let twisted_x = { + let b: Fq = reader.read_value()?; + let a: Fq = reader.read_value()?; + Fq2::new(a, b) + }; + let twisted_y = { + let b: Fq = reader.read_value()?; + let a: Fq = reader.read_value()?; + Fq2::new(a, b) + }; + + let twisted = { + if twisted_x.is_zero() && twisted_y.is_zero() { + G2::zero() + } else { + AffineG2::new(twisted_x, twisted_y)?.into() + } + }; + + let a = { + if x.is_zero() && y.is_zero() { + substrate_bn::G1::zero() + } else { + AffineG1::new(x, y)?.into() + } + }; + + Ok((a, twisted)) + } + + const GROUP_BYTE_LEN: usize = 192; + + // This precompile is strange in that it doesn't automatically "pad" the input. + // So we have to check the sizes. + if input.len() % GROUP_BYTE_LEN != 0 { + return Err(PrecompileError::IncorrectInputSize); + } + + let mut groups = Vec::new(); + for i in 0..input.len() / GROUP_BYTE_LEN { + let offset = i * GROUP_BYTE_LEN; + groups.push(read_group(&input[offset..offset + GROUP_BYTE_LEN])?); + } + + let accumulated = pairing_batch(&groups); + + let paring_success = if accumulated == Gt::one() { U256::one() } else { U256::zero() }; + let mut ret = [0u8; EVM_WORD_SIZE]; + paring_success.to_big_endian(&mut ret); + Ok(ret.to_vec()) +} + +/// https://eips.ethereum.org/EIPS/eip-152 +pub(super) fn blake2f( + _: &mut System, + input: &[u8], + _: PrecompileContext, +) -> PrecompileResult { + if input.len() != 213 { + return Err(PrecompileError::IncorrectInputSize); + } + let mut rounds = [0u8; 4]; + + let mut start = 0; + + // 4 bytes + rounds.copy_from_slice(&input[..4]); + start += 4; + // 64 bytes + let h = &input[start..start + 64]; + start += 64; + // 128 bytes + let m = &input[start..start + 128]; + start += 128; + // 16 bytes + let t = &input[start..start + 16]; + start += 16; + + debug_assert_eq!(start, 212, "expected start to be at the last byte"); + let f = match input[start] { + 0 => Ok(false), + 1 => Ok(true), + _ => Err(PrecompileError::IncorrectInputSize), + }?; + + let rounds = u32::from_be_bytes(rounds); + let mut h = { + let mut ret = [0u64; 8]; + LE::read_u64_into(h, &mut ret); + ret + }; + let m = { + let mut ret = [0u64; 16]; + LE::read_u64_into(m, &mut ret); + ret + }; + let t = { + let mut ret = [0u64; 2]; + LE::read_u64_into(t, &mut ret); + ret + }; + + super::blake2f_impl::compress(&mut h, m, t, f, rounds as usize); + + let mut output = vec![0; 64]; + LE::write_u64_into(&h, &mut output); + Ok(output) +} + +#[cfg(test)] +mod tests { + use crate::interpreter::CallKind; + + use super::*; + use fil_actors_runtime::test_utils::MockRuntime; + use hex_literal::hex; + + impl Default for PrecompileContext { + fn default() -> Self { + Self { call_type: CallKind::Call, gas_limit: u64::MAX, value: U256::ZERO } + } + } + + #[test] + fn bn_recover() { + let mut rt = MockRuntime::default(); + let mut system = System::create(&mut rt).unwrap(); + + let input = &hex!( + "456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3" // h(ash) + "000000000000000000000000000000000000000000000000000000000000001c" // v (recovery byte) + // signature + "9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608" // r + "4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada" // s + ); + + let expected = hex!("0000000000000000000000007156526fbd7a3c72969b54f64e42c10fbb768c8a"); + let res = ec_recover(&mut system, input, PrecompileContext::default()).unwrap(); + assert_eq!(&res, &expected); + + let input = &hex!( + "456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3" // h(ash) + "000000000000000000000000000000000000000000000000000000000000001c" // v (recovery byte) + // signature + "0000005bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608" // r + "4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada" // s + ); + // wrong signature + let res = ec_recover(&mut system, input, PrecompileContext::default()).unwrap(); + assert!(res.is_empty()); + + let input = &hex!( + "456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3" // h(ash) + "000000000000000000000000000000000000000000000000000000000000000a" // v (recovery byte) + // signature + "0000005bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608" // r + "4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada" // s + ); + // invalid recovery byte + let res = ec_recover(&mut system, input, PrecompileContext::default()).unwrap(); + assert!(res.is_empty()); + } + + #[test] + fn sha256() { + use super::sha256 as hash; + let input = "foo bar baz boxy".as_bytes(); + + let mut rt = MockRuntime::default(); + let mut system = System::create(&mut rt).unwrap(); + + let expected = hex!("ace8597929092c14bd028ede7b07727875788c7e130278b5afed41940d965aba"); + let res = hash(&mut system, input, PrecompileContext::default()).unwrap(); + assert_eq!(&res, &expected); + } + + #[test] + fn ripemd160() { + use super::ripemd160 as hash; + let input = "foo bar baz boxy".as_bytes(); + + let mut rt = MockRuntime::default(); + let mut system = System::create(&mut rt).unwrap(); + + let expected = hex!("0000000000000000000000004cd7a0452bd3d682e4cbd5fa90f446d7285b156a"); + let res = hash(&mut system, input, PrecompileContext::default()).unwrap(); + assert_eq!(&res, &expected); + } + + #[test] + fn mod_exponent() { + let input = &hex!( + "0000000000000000000000000000000000000000000000000000000000000001" // base len + "0000000000000000000000000000000000000000000000000000000000000001" // exp len + "0000000000000000000000000000000000000000000000000000000000000001" // mod len + "08" // base + "09" // exp + "0A" // mod + ); + + let mut rt = MockRuntime::default(); + let mut system = System::create(&mut rt).unwrap(); + + let expected = hex!("08"); + let res = modexp(&mut system, input, PrecompileContext::default()).unwrap(); + assert_eq!(&res, &expected); + + let input = &hex!( + "0000000000000000000000000000000000000000000000000000000000000004" // base len + "0000000000000000000000000000000000000000000000000000000000000002" // exp len + "0000000000000000000000000000000000000000000000000000000000000006" // mod len + "12345678" // base + "1234" // exp + "012345678910" // mod + ); + let expected = hex!("00358eac8f30"); // left padding & 230026940208 + let res = modexp(&mut system, input, PrecompileContext::default()).unwrap(); + assert_eq!(&res, &expected); + + let expected = hex!("000000"); // invalid values will just be [0; mod_len] + let input = &hex!( + "0000000000000000000000000000000000000000000000000000000000000001" // base len + "0000000000000000000000000000000000000000000000000000000000000002" // exp len + "0000000000000000000000000000000000000000000000000000000000000003" // mod len + "01" // base + "02" // exp + "03" // mod + ); + // input smaller than expected + let res = modexp(&mut system, input, PrecompileContext::default()).unwrap(); + assert_eq!(&res, &expected); + + let input = &hex!( + "0000000000000000000000000000000000000000000000000000000000000001" // base len + "0000000000000000000000000000000000000000000000000000000000000001" // exp len + "0000000000000000000000000000000000000000000000000000000000000000" // mod len + "08" // base + "09" // exp + ); + // no mod is invalid + let res = modexp(&mut system, input, PrecompileContext::default()).unwrap(); + assert!(res.is_empty()); + } + + // bn tests borrowed from https://github.com/bluealloy/revm/blob/26540bf5b29de6e7c8020c4c1880f8a97d1eadc9/crates/revm_precompiles/src/bn128.rs + mod bn { + use serde::{Deserialize, Serialize}; + + use super::MockRuntime; + + use crate::interpreter::{ + precompiles::{ + ec_add, ec_mul, ec_pairing, + evm::{blake2f, ec_recover, modexp}, + PrecompileContext, PrecompileError, PrecompileFn, + }, + System, + }; + + #[test] + fn bn_add() { + let mut rt = MockRuntime::default(); + let mut system = System::create(&mut rt).unwrap(); + + let input = hex::decode( + "\ + 18b18acfb4c2c30276db5411368e7185b311dd124691610c5d3b74034e093dc9\ + 063c909c4720840cb5134cb9f59fa749755796819658d32efc0d288198f37266\ + 07c2b7f58a84bd6145f00c9c2bc0bb1a187f20ff2c92963a88019e7c6a014eed\ + 06614e20c147e940f2d70da3f74c9a17df361706a4485c742bd6788478fa17d7", + ) + .unwrap(); + let expected = hex::decode( + "\ + 2243525c5efd4b9c3d3c45ac0ca3fe4dd85e830a4ce6b65fa1eeaee202839703\ + 301d1d33be6da8e509df21cc35964723180eed7532537db9ae5e7d48f195c915", + ) + .unwrap(); + let res = ec_add(&mut system, &input, PrecompileContext::default()).unwrap(); + assert_eq!(res, expected); + // zero sum test + let input = vec![0; 32 * 4]; + let res = ec_add(&mut system, &input, PrecompileContext::default()).unwrap(); + assert_eq!(res, vec![0; 64]); + + // no input test (auto zero extend) + let input = []; + let res = ec_add(&mut system, &input, PrecompileContext::default()).unwrap(); + assert_eq!(res, vec![0; 64]); + + // point not on curve fail + let input = hex::decode( + "\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111", + ) + .unwrap(); + let res = ec_add(&mut system, &input, PrecompileContext::default()); + assert!(matches!( + res, + Err(PrecompileError::EcErr(substrate_bn::CurveError::NotMember)) + )); + } + + const TESTDATA_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/precompile-testdata/"); + + #[test] + fn conformance() { + #[derive(Deserialize, Serialize)] + #[serde(rename_all = "PascalCase")] + struct TestCase { + name: String, + #[serde(with = "hex")] + input: Vec, + #[serde(with = "hex")] + expected: Vec, + } + + let tests: &[(PrecompileFn, &'static str)] = &[ + (ec_add, "bn256Add"), + (ec_mul, "bn256ScalarMul"), + (ec_recover, "ecRecover"), + (blake2f, "blake2F"), + (ec_pairing, "bn256Pairing"), + (modexp, "modexp"), + (modexp, "modexp_eip2565"), + ]; + + let mut rt = MockRuntime::default(); + let mut system = System::create(&mut rt).unwrap(); + + for (f, name) in tests { + let td = std::fs::read_to_string(format!("{TESTDATA_PATH}/{name}.json")).unwrap(); + let cases: Vec = serde_json::from_str(&td).unwrap(); + for t in cases { + let res = f(&mut system, &t.input, PrecompileContext::default()) + .expect("call failed"); + assert_eq!(res, t.expected); + } + } + } + + #[test] + fn conformance_failure() { + #[derive(Deserialize, Serialize)] + #[serde(rename_all = "PascalCase")] + struct FailureCase { + name: String, + #[serde(with = "hex")] + input: Vec, + } + + let failures: &[(PrecompileFn, &'static str)] = + &[(blake2f, "fail-blake2f")]; + + let mut rt = MockRuntime::default(); + let mut system = System::create(&mut rt).unwrap(); + + for (f, name) in failures { + let td = std::fs::read_to_string(format!("{TESTDATA_PATH}/{name}.json")).unwrap(); + let cases: Vec = serde_json::from_str(&td).unwrap(); + for t in cases { + let _ = f(&mut system, &t.input, PrecompileContext::default()) + .expect_err("call failed"); + } + } + } + + #[test] + fn bn_mul() { + let mut rt = MockRuntime::default(); + let mut system = System::create(&mut rt).unwrap(); + + let input = hex::decode( + "\ + 2bd3e6d0f3b142924f5ca7b49ce5b9d54c4703d7ae5648e61d02268b1a0a9fb7\ + 21611ce0a6af85915e2f1d70300909ce2e49dfad4a4619c8390cae66cefdb204\ + 00000000000000000000000000000000000000000000000011138ce750fa15c2", + ) + .unwrap(); + let expected = hex::decode( + "\ + 070a8d6a982153cae4be29d434e8faef8a47b274a053f5a4ee2a6c9c13c31e5c\ + 031b8ce914eba3a9ffb989f9cdd5b0f01943074bf4f0f315690ec3cec6981afc", + ) + .unwrap(); + let res = ec_mul(&mut system, &input, PrecompileContext::default()).unwrap(); + assert_eq!(res, expected); + + // no input test + let input = [0u8; 0]; + let res = ec_mul(&mut system, &input, PrecompileContext::default()).unwrap(); + assert_eq!(res, vec![0u8; 64]); + + // point not on curve fail + let input = hex::decode( + "\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 0f00000000000000000000000000000000000000000000000000000000000000", + ) + .unwrap(); + let res = ec_mul(&mut system, &input, PrecompileContext::default()); + assert!(matches!( + res, + Err(PrecompileError::EcErr(substrate_bn::CurveError::NotMember)) + )); + + let input = hex::decode( + "\ + 035cf447ec2f8f21e6ea3d49d80a4a823834b1a776ab1733731587613f5065f8\ + 21c972c4e0c8eb2430171599b1f4900601fdb8f4b2d248d22ebefe3d5368a800\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + ", + ) + .unwrap(); + let res = ec_mul(&mut system, &input, PrecompileContext::default()); + assert_eq!(vec![0u8; 64], res.unwrap()); + } + + #[test] + fn bn_pair() { + let mut rt = MockRuntime::default(); + let mut system = System::create(&mut rt).unwrap(); + + let input = hex::decode( + "\ + 1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f59\ + 3034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41\ + 209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7\ + 04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678\ + 2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d\ + 120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550\ + 111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c\ + 2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411\ + 198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\ + 1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\ + 090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b\ + 12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + ) + .unwrap(); + + let expected = + hex::decode("0000000000000000000000000000000000000000000000000000000000000001") + .unwrap(); + + let res = ec_pairing(&mut system, &input, PrecompileContext::default()).unwrap(); + assert_eq!(res, expected); + + // out of gas test + let input = hex::decode( + "\ + 1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f59\ + 3034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41\ + 209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7\ + 04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678\ + 2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d\ + 120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550\ + 111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c\ + 2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411\ + 198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\ + 1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\ + 090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b\ + 12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + ) + .unwrap(); + let res = ec_pairing(&mut system, &input, PrecompileContext::default()).unwrap(); + assert_eq!(res, expected); + // no input test + let input = [0u8; 0]; + let expected = + hex::decode("0000000000000000000000000000000000000000000000000000000000000001") + .unwrap(); + let res = ec_pairing(&mut system, &input, PrecompileContext::default()).unwrap(); + assert_eq!(res, expected); + // point not on curve fail + let input = hex::decode( + "\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111", + ) + .unwrap(); + let res = ec_pairing(&mut system, &input, PrecompileContext::default()); + assert!(matches!( + res, + Err(PrecompileError::EcErr(substrate_bn::CurveError::NotMember)) + )); + // invalid input length + let input = hex::decode( + "\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 111111111111111111111111111111\ + ", + ) + .unwrap(); + let res = ec_pairing(&mut system, &input, PrecompileContext::default()); + assert!(matches!(res, Err(PrecompileError::IncorrectInputSize))); + } + } + + // https://eips.ethereum.org/EIPS/eip-152#test-cases + #[test] + fn blake2() { + use super::blake2f; + let mut rt = MockRuntime::default(); + let mut system = System::create(&mut rt).unwrap(); + + // // helper to turn EIP test cases into something readable + // fn test_case_formatter(mut remaining: impl ToString) { + // let mut rounds = remaining.to_string(); + // let mut h = rounds.split_off(2*4); + // let mut m = h.split_off(2*64); + // let mut t_0 = m.split_off(2*128); + // let mut t_1 = t_0.split_off(2*8); + // let mut f = t_1.split_off(2*8); + + // println!(" + // \"{rounds}\" + // \"{h}\" + // \"{m}\" + // \"{t_0}\" + // \"{t_1}\" + // \"{f}\" + // ") + // } + + // T0 invalid input len + assert!(matches!( + blake2f(&mut system, &[], PrecompileContext::default()), + Err(PrecompileError::IncorrectInputSize) + )); + + // T1 too small + let input = &hex!( + "00000c" + "48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b" + "6162630000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0300000000000000" + "0000000000000000" + "02" + ); + assert!(matches!( + blake2f(&mut system, input, PrecompileContext::default()), + Err(PrecompileError::IncorrectInputSize) + )); + + // T2 too large + let input = &hex!( + "000000000c" + "48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b" + "6162630000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0300000000000000" + "0000000000000000" + "02" + ); + assert!(matches!( + blake2f(&mut system, input, PrecompileContext::default()), + Err(PrecompileError::IncorrectInputSize) + )); + + // T3 final block indicator invalid + let input = &hex!( + "0000000c" + "48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b" + "6162630000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0300000000000000" + "0000000000000000" + "02" + ); + assert!(matches!( + blake2f(&mut system, input, PrecompileContext::default()), + Err(PrecompileError::IncorrectInputSize) + )); + + // outputs + + // T4 + let expected = hex!("08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d282e6ad7f520e511f6c3e2b8c68059b9442be0454267ce079217e1319cde05b"); + let input = &hex!( + "00000000" + "48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b" + "6162630000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0300000000000000" + "0000000000000000" + "01" + ); + assert!( + matches!(blake2f(&mut system, input, PrecompileContext::default()), Ok(v) if v == expected) + ); + + // T5 + let expected = &hex!("ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923"); + let input = &hex!( + "0000000c" + "48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b" + "6162630000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0300000000000000" + "0000000000000000" + "01" + ); + assert!( + matches!(blake2f(&mut system, input, PrecompileContext::default()), Ok(v) if v == expected) + ); + + // T6 + let expected = &hex!("75ab69d3190a562c51aef8d88f1c2775876944407270c42c9844252c26d2875298743e7f6d5ea2f2d3e8d226039cd31b4e426ac4f2d3d666a610c2116fde4735"); + let input = &hex!( + "0000000c" + "48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b" + "6162630000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0300000000000000" + "0000000000000000" + "00" + ); + assert!( + matches!(blake2f(&mut system, input, PrecompileContext::default()), Ok(v) if v == expected) + ); + + // T7 + let expected = &hex!("b63a380cb2897d521994a85234ee2c181b5f844d2c624c002677e9703449d2fba551b3a8333bcdf5f2f7e08993d53923de3d64fcc68c034e717b9293fed7a421"); + let input = &hex!( + "00000001" + "48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b" + "6162630000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0300000000000000" + "0000000000000000" + "01" + ); + assert!( + matches!(blake2f(&mut system, input, PrecompileContext::default()), Ok(v) if v == expected) + ); + + // T8 + // NOTE: + // original test case ran ffffffff rounds of blake2b + // with an expected output of fc59093aafa9ab43daae0e914c57635c5402d8e3d2130eb9b3cc181de7f0ecf9b22bf99a7815ce16419e200e01846e6b5df8cc7703041bbceb571de6631d2615 + // I ran this successfully while grabbing a cup of coffee, so if you fee like wasting u32::MAX rounds of hash time, (25-ish min on Ryzen5 2600) you can test it as such. + // For my and CI's sanity however, we are capping it at 0000ffff. + let expected = &hex!("183ed9b1e5594bcdd715a4e4fd7b0dc2eaa2ef9bda48242af64c687081142156621bc94bb2d5aa99d83c2f1a5d9c426e1b6a1755a5e080f6217e2a5f3b9c4624"); + let input = &hex!( + "0000ffff" + "48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b" + "6162630000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0300000000000000" + "0000000000000000" + "01" + ); + assert!( + matches!(blake2f(&mut system, input, PrecompileContext::default()), Ok(v) if v == expected) + ); + } +} diff --git a/actors/evm/src/interpreter/precompiles/fvm.rs b/actors/evm/src/interpreter/precompiles/fvm.rs new file mode 100644 index 000000000..a9121e384 --- /dev/null +++ b/actors/evm/src/interpreter/precompiles/fvm.rs @@ -0,0 +1,224 @@ +use crate::{EVM_MAX_RESERVED_METHOD, EVM_WORD_SIZE}; +use fil_actors_evm_shared::uints::U256; +use fil_actors_runtime::runtime::Runtime; +use fvm_ipld_encoding::ipld_block::IpldBlock; +use fvm_shared::{address::Address, econ::TokenAmount, sys::SendFlags, METHOD_SEND}; + +use crate::interpreter::{CallKind, System}; + +use super::{PrecompileContext, PrecompileError, PrecompileResult}; +use crate::reader::ValueReader; + +/// Read BE encoded low u64 ID address from a u256 word +/// Looks up and returns the encoded f4 addresses of an ID address. Empty array if not found or `InvalidInput` input was larger 2^64. +pub(super) fn lookup_delegated_address( + system: &mut System, + input: &[u8], + _: PrecompileContext, +) -> PrecompileResult { + let mut id_bytes = ValueReader::new(input); + let id = id_bytes.read_value::()?; + + let address = system.rt.lookup_delegated_address(id); + let ab = match address { + Some(a) => a.to_bytes(), + None => Vec::new(), + }; + Ok(ab) +} + +/// Reads a FIL (i.e. f0xxx, f4xfxxx) encoded address +/// Resolves a FIL encoded address into an ID address +/// Returns BE encoded u256 (return will always be under 2^64). +/// Empty array if nothing found or `InvalidInput` if length was larger 2^32 or Address parsing failed. +pub(super) fn resolve_address( + system: &mut System, + input: &[u8], + _: PrecompileContext, +) -> PrecompileResult { + let addr = match Address::from_bytes(input) { + Ok(o) => o, + Err(e) => { + log::debug!(target: "evm", "Address parsing failed: {e}"); + return Err(PrecompileError::InvalidInput); + } + }; + Ok(system + .rt + .resolve_address(&addr) + .map(|a| { + log::debug!(target: "evm", "{addr} resolved to {a}"); + U256::from(a).to_bytes().to_vec() + }) + .unwrap_or_default()) +} + +/// Calls an actor by address. +/// +/// Parameters are encoded according to the solidity ABI, with no function selector: +/// +/// ```text +/// u64 method +/// u256 value +/// u64 flags (1 for read-only, 0 otherwise) +/// u64 codec (0x71 for "dag-cbor", or `0` for "nothing") +/// bytes params (must be empty if the codec is 0x0) +/// bytes address +/// ``` +/// +/// Returns (also solidity ABI encoded): +/// +/// ```text +/// i256 exit_code +/// u64 codec +/// bytes return_value +/// ``` +/// +/// for exit_code: +/// - negative values are system errors +/// - positive are user errors (from the called actor) +/// - 0 is success +pub(super) fn call_actor( + system: &mut System, + input: &[u8], + ctx: PrecompileContext, +) -> PrecompileResult { + call_actor_shared(system, input, ctx, false) +} + +/// Calls an actor by the actor's actor ID. +/// +/// Parameters are encoded according to the solidity ABI, with no function selector: +/// +/// ```text +/// u64 method +/// u256 value +/// u64 flags (1 for read-only, 0 otherwise) +/// u64 codec (0x71 for "dag-cbor", or `0` for "nothing") +/// bytes params (must be empty if the codec is 0x0) +/// u64 actor_id +/// ``` +/// +/// Returns (also solidity ABI encoded): +/// +/// ```text +/// i256 exit_code +/// u64 codec +/// bytes return_value +/// ``` +/// +/// for exit_code: +/// - negative values are system errors +/// - positive are user errors (from the called actor) +/// - 0 is success +pub(super) fn call_actor_id( + system: &mut System, + input: &[u8], + ctx: PrecompileContext, +) -> PrecompileResult { + call_actor_shared(system, input, ctx, true) +} + +pub(super) fn call_actor_shared( + system: &mut System, + input: &[u8], + ctx: PrecompileContext, + by_id: bool, +) -> PrecompileResult { + // ----- Input Parameters ------- + + if ctx.call_type != CallKind::DelegateCall { + return Err(PrecompileError::CallForbidden); + } + + let mut input_params = ValueReader::new(input); + + let method: u64 = input_params.read_value()?; + + let value: U256 = input_params.read_value()?; + + let flags: u64 = input_params.read_value()?; + let flags = SendFlags::from_bits(flags).ok_or(PrecompileError::InvalidInput)?; + + let codec: u64 = input_params.read_value()?; + + let params_off: u32 = input_params.read_value()?; + let id_or_addr_off: u64 = input_params.read_value()?; + + input_params.seek(params_off.try_into()?); + let params_len: u32 = input_params.read_value()?; + let params = input_params.read_padded(params_len.try_into()?); + + let address = if by_id { + Address::new_id(id_or_addr_off) + } else { + input_params.seek(id_or_addr_off.try_into()?); + let addr_len: u32 = input_params.read_value()?; + let addr_bytes = input_params + .read_padded(addr_len.try_into().map_err(|_| PrecompileError::InvalidInput)?); + Address::from_bytes(&addr_bytes).map_err(|_| PrecompileError::InvalidInput)? + }; + + if method <= EVM_MAX_RESERVED_METHOD && method != METHOD_SEND { + return Err(PrecompileError::InvalidInput); + } + + // ------ Begin Call ------- + + let result = { + // TODO only CBOR or "nothing" for now. We should support RAW and DAG_CBOR in the future. + let params = match codec { + fvm_ipld_encoding::CBOR => Some(IpldBlock { codec, data: params.into() }), + #[cfg(feature = "hyperspace")] + fvm_ipld_encoding::DAG_CBOR => Some(IpldBlock { codec, data: params.into() }), + 0 if params.is_empty() => None, + _ => return Err(PrecompileError::InvalidInput), + }; + // This method returns two results. If the outer result is an error, we consider the + // precompile to have completely failed. + // + // If get `Ok(anything)`, we expose `anything` to the user. + system.send_raw( + &address, + method, + params, + TokenAmount::from(&value), + Some(ctx.gas_limit), + flags, + )? + }; + + // ------ Build Output ------- + + let output = { + // negative values are syscall errors + // positive values are user/actor errors + // success is 0 + let (exit_code, data) = match result { + Err(errno) => { + let exit_code = U256::from(errno as u32).i256_neg(); + + // no return only exit code + (exit_code, None) + } + Ok(resp) => (U256::from(resp.exit_code.value()), resp.return_data), + }; + + let ret_blk = data.unwrap_or(IpldBlock { codec: 0, data: vec![] }); + + let mut output = Vec::with_capacity(4 * EVM_WORD_SIZE + ret_blk.data.len()); + output.extend_from_slice(&exit_code.to_bytes()); + output.extend_from_slice(&U256::from(ret_blk.codec).to_bytes()); + output.extend_from_slice(&U256::from(output.len() + EVM_WORD_SIZE).to_bytes()); + output.extend_from_slice(&U256::from(ret_blk.data.len()).to_bytes()); + output.extend_from_slice(&ret_blk.data); + // Pad out to the next increment of 32 bytes for solidity compatibility. + let offset = output.len() % EVM_WORD_SIZE; + if offset > 0 { + output.resize(output.len() - offset + EVM_WORD_SIZE, 0); + } + output + }; + + Ok(output) +} diff --git a/actors/evm/src/interpreter/precompiles/mod.rs b/actors/evm/src/interpreter/precompiles/mod.rs new file mode 100644 index 000000000..5faf7f68d --- /dev/null +++ b/actors/evm/src/interpreter/precompiles/mod.rs @@ -0,0 +1,224 @@ +use std::{marker::PhantomData, num::TryFromIntError}; + +use fil_actors_evm_shared::{address::EthAddress, uints::U256}; +use fil_actors_runtime::{runtime::Runtime, ActorError}; +use fvm_shared::{address::Address, econ::TokenAmount}; +use substrate_bn::{CurveError, FieldError, GroupError}; + +use crate::reader::OverflowError; + +use super::{CallKind, System}; +mod blake2f_impl; +mod evm; +mod fvm; + +use evm::{blake2f, ec_add, ec_mul, ec_pairing, ec_recover, identity, modexp, ripemd160, sha256}; +use fvm::{call_actor, call_actor_id, lookup_delegated_address, resolve_address}; + +type PrecompileFn = fn(&mut System, &[u8], PrecompileContext) -> PrecompileResult; +pub type PrecompileResult = Result, PrecompileError>; + +pub const NATIVE_PRECOMPILE_ADDRESS_PREFIX: u8 = 0xFE; + +struct PrecompileTable([Option>; N]); + +impl PrecompileTable { + /// Tries to lookup Precompile, None if empty slot or out of bounds. + /// Last byte of precompile address - 1 is the index. + fn get(&self, index: usize) -> Option> { + self.0.get(index).and_then(|i| i.as_ref()).copied() + } +} + +pub fn is_reserved_precompile_address(addr: &EthAddress) -> bool { + let [prefix, middle @ .., index] = addr.0; + (prefix == 0x00 || prefix == NATIVE_PRECOMPILE_ADDRESS_PREFIX) + && middle == [0u8; 18] + && index > 0 +} + +pub struct Precompiles(PhantomData); + +impl Precompiles { + /// FEVM specific precompiles (0xfe prefix) + const NATIVE_PRECOMPILES: PrecompileTable = PrecompileTable([ + Some(resolve_address::), // 0xfe00..01 + Some(lookup_delegated_address::), // 0xfe00..02 + Some(call_actor::), // 0xfe00..03 + None, // 0xfe00..04 DISABLED + Some(call_actor_id::), // 0xfe00..05 + ]); + + /// EVM specific precompiles + const EVM_PRECOMPILES: PrecompileTable = PrecompileTable([ + Some(ec_recover::), // 0x01 ecrecover + Some(sha256::), // 0x02 SHA2-256 + Some(ripemd160::), // 0x03 ripemd160 + Some(identity::), // 0x04 identity + Some(modexp::), // 0x05 modexp + Some(ec_add::), // 0x06 ecAdd + Some(ec_mul::), // 0x07 ecMul + Some(ec_pairing::), // 0x08 ecPairing + Some(blake2f::), // 0x09 blake2f + ]); + + fn lookup_precompile(addr: &EthAddress) -> Option> { + let [prefix, _m @ .., index] = addr.0; + if is_reserved_precompile_address(addr) { + let index = index as usize - 1; + match prefix { + NATIVE_PRECOMPILE_ADDRESS_PREFIX => Self::NATIVE_PRECOMPILES.get(index), + 0x00 => Self::EVM_PRECOMPILES.get(index), + _ => None, + } + } else { + None + } + } + + /// Call the specified precompile. This will automatically transfer any value (if non-zero) to + /// the target contract. + pub fn call_precompile( + system: &mut System, + precompile_addr: &EthAddress, + input: &[u8], + context: PrecompileContext, + ) -> PrecompileResult { + // First, try to call the precompile, if defined. + let result = Self::lookup_precompile(precompile_addr) + .map(|precompile_fn| precompile_fn(system, input, context)) + .transpose()? + .unwrap_or_default(); + // Then transfer the value. We do this second because we don't want to transfer if the + // precompile reverts. + // + // This shouldn't be observable as the only precompile with side-effects is the call_actor + // precompile, and that precompile can only be called with delegatecall. + if !context.value.is_zero() { + let fil_addr: Address = precompile_addr.into(); + system + .transfer(&fil_addr, TokenAmount::from(&context.value)) + .map_err(|_| PrecompileError::TransferFailed)?; + } + Ok(result) + } + + /// Checks if word is an existing precompile + #[inline] + pub fn is_precompile(addr: &EthAddress) -> bool { + !addr.is_null() && Self::lookup_precompile(addr).is_some() + } +} + +#[derive(Debug)] +pub enum PrecompileError { + // EVM precompile errors + EcErr(CurveError), + IncorrectInputSize, + // FVM precompile errors + InvalidInput, + CallForbidden, + TransferFailed, + VMError(ActorError), +} + +impl From for PrecompileError { + fn from(e: ActorError) -> Self { + Self::VMError(e) + } +} +impl From for PrecompileError { + fn from(_: TryFromIntError) -> Self { + Self::InvalidInput + } +} + +impl From for PrecompileError { + fn from(_: OverflowError) -> Self { + PrecompileError::InvalidInput + } +} + +impl From for PrecompileError { + fn from(src: FieldError) -> Self { + PrecompileError::EcErr(src.into()) + } +} + +impl From for PrecompileError { + fn from(src: CurveError) -> Self { + PrecompileError::EcErr(src) + } +} + +impl From for PrecompileError { + fn from(_: GroupError) -> Self { + PrecompileError::EcErr(CurveError::NotMember) + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub struct PrecompileContext { + pub call_type: CallKind, + pub gas_limit: u64, + pub value: U256, +} + +#[cfg(test)] +mod test { + use fil_actors_evm_shared::address::EthAddress; + use fil_actors_runtime::test_utils::MockRuntime; + + use crate::interpreter::precompiles::is_reserved_precompile_address; + + use super::Precompiles; + + #[test] + fn is_native_precompile() { + let addr = EthAddress(hex_literal::hex!("fe00000000000000000000000000000000000001")); + assert!(Precompiles::::is_precompile(&addr)); + assert!(is_reserved_precompile_address(&addr)); + } + + #[test] + fn is_evm_precompile() { + let addr = EthAddress(hex_literal::hex!("0000000000000000000000000000000000000001")); + assert!(Precompiles::::is_precompile(&addr)); + assert!(is_reserved_precompile_address(&addr)); + } + + #[test] + fn is_over_precompile() { + let addr = EthAddress(hex_literal::hex!("ff00000000000000000000000000000000000001")); + assert!(!Precompiles::::is_precompile(&addr)); + assert!(!is_reserved_precompile_address(&addr)); + } + + #[test] + fn zero_addr_precompile() { + let eth_addr = EthAddress(hex_literal::hex!("fe00000000000000000000000000000000000000")); + let native_addr = EthAddress(hex_literal::hex!("0000000000000000000000000000000000000000")); + assert!(!Precompiles::::is_precompile(ð_addr)); + assert!(!Precompiles::::is_precompile(&native_addr)); + assert!(!is_reserved_precompile_address(ð_addr)); + assert!(!is_reserved_precompile_address(&native_addr)); + } + + #[test] + fn between_precompile() { + let addr = EthAddress(hex_literal::hex!("a000000000000000000000000000000000000001")); + assert!(!Precompiles::::is_precompile(&addr)); + assert!(!is_reserved_precompile_address(&addr)); + } + + #[test] + fn bad_index() { + let eth_addr = EthAddress(hex_literal::hex!("fe00000000000000000000000000000000000020")); + let native_addr = EthAddress(hex_literal::hex!("0000000000000000000000000000000000000020")); + assert!(!Precompiles::::is_precompile(ð_addr)); + assert!(!Precompiles::::is_precompile(&native_addr)); + // reserved doesn't check index is within range + assert!(is_reserved_precompile_address(ð_addr)); + assert!(is_reserved_precompile_address(&native_addr)); + } +} diff --git a/actors/evm/src/interpreter/stack.rs b/actors/evm/src/interpreter/stack.rs new file mode 100644 index 000000000..73a19e3f8 --- /dev/null +++ b/actors/evm/src/interpreter/stack.rs @@ -0,0 +1,255 @@ +#![allow(clippy::missing_safety_doc)] + +use fil_actors_evm_shared::uints::U256; +use fil_actors_runtime::{ActorError, AsActorError}; + +use crate::{EVM_CONTRACT_STACK_OVERFLOW, EVM_CONTRACT_STACK_UNDERFLOW}; + +/// Ethereum Yellow Paper (9.1) +pub const STACK_SIZE: usize = 1024; + +const INITIAL_STACK_SIZE: usize = 32; + +/// EVM stack. +#[derive(Clone, Debug)] +pub struct Stack { + stack: Vec, +} + +impl Stack { + #[inline] + pub fn new() -> Self { + Stack { stack: Vec::with_capacity(INITIAL_STACK_SIZE) } + } + + #[inline(always)] + pub fn len(&self) -> usize { + self.stack.len() + } + + #[inline(always)] + pub fn is_empty(&self) -> bool { + self.stack.is_empty() + } + + #[inline(always)] + pub fn push_unchecked(&mut self, value: U256) { + self.stack.push(value); + } + + #[inline(always)] + pub fn push(&mut self, value: U256) -> Result<(), ActorError> { + if self.stack.len() >= STACK_SIZE { + Err(ActorError::unchecked(EVM_CONTRACT_STACK_OVERFLOW, "stack overflow".into())) + } else { + self.stack.push(value); + Ok(()) + } + } + + #[inline] + pub fn pop_many(&mut self) -> Result<&[U256; S], ActorError> { + if self.len() < S { + return Err(ActorError::unchecked( + EVM_CONTRACT_STACK_UNDERFLOW, + "stack underflow".into(), + )); + } + let new_len = self.len() - S; + unsafe { + // This is safe because: + // + // 1. U256 isn't drop. + // 2. The borrow will end before we can do anything else. + // + // It's faster than copying these elements multiple times. + self.stack.set_len(new_len); + Ok(&*(self.stack.as_ptr().add(new_len) as *const [U256; S])) + } + } + + #[inline(always)] + /// Ensures at least one more item is able to be allocated on the stack. + pub fn ensure_one(&self) -> Result<(), ActorError> { + if self.stack.len() >= STACK_SIZE { + Err(ActorError::unchecked(EVM_CONTRACT_STACK_OVERFLOW, "stack overflow".into())) + } else { + Ok(()) + } + } + + #[inline] + /// Duplicates and pushes value of index `i - 1` to the top of the stack. + /// + /// Returns error if there is a stack underflow/overflow. + /// + /// Panics if `i == 0`, -1 is not a valid index. + pub fn dup(&mut self, i: usize) -> Result<(), ActorError> { + assert!(i > 0); + let len = self.stack.len(); + if len >= STACK_SIZE { + Err(ActorError::unchecked(EVM_CONTRACT_STACK_OVERFLOW, "stack overflow".into())) + } else if i > len { + Err(ActorError::unchecked(EVM_CONTRACT_STACK_UNDERFLOW, "stack underflow".into())) + } else { + unsafe { + // This is safe because we're careful not to alias. We're _basically_ implementing + // "emplace", because rust still doesn't have it. + // + // Yes, this is faster than a get/push. + self.stack.reserve(1); + *self.stack.as_mut_ptr().add(len) = *self.stack.get_unchecked(len - i); + self.stack.set_len(len + 1); + } + Ok(()) + } + } + + #[inline] + pub fn swap_top(&mut self, i: usize) -> Result<(), ActorError> { + let len = self.stack.len(); + if len <= i { + return Err(ActorError::unchecked( + EVM_CONTRACT_STACK_UNDERFLOW, + "stack underflow".into(), + )); + } + self.stack.swap(len - i - 1, len - 1); + Ok(()) + } + + #[inline] + pub fn pop(&mut self) -> Result { + self.stack.pop().context_code(EVM_CONTRACT_STACK_UNDERFLOW, "stack underflow") + } + + #[inline] + pub fn drop(&mut self) -> Result<(), ActorError> { + if self.stack.pop().is_some() { + Ok(()) + } else { + Err(ActorError::unchecked(EVM_CONTRACT_STACK_UNDERFLOW, "stack underflow".into())) + } + } +} + +impl Default for Stack { + fn default() -> Self { + Self::new() + } +} + +#[test] +fn test_stack_push_pop() { + let mut stack = Stack::new(); + stack.push(1.into()).unwrap(); + stack.push(2.into()).unwrap(); + assert_eq!(stack.pop().unwrap(), 2); + assert_eq!(stack.pop().unwrap(), 1); +} + +#[test] +fn test_stack_drop() { + let mut stack = Stack::new(); + stack.push(1.into()).unwrap(); + stack.push(2.into()).unwrap(); + stack.drop().unwrap(); + stack.drop().unwrap(); + assert_eq!( + stack.drop().expect_err("expect underflow").exit_code(), + EVM_CONTRACT_STACK_UNDERFLOW + ); +} + +#[test] +fn test_stack_swap() { + let mut stack = Stack::new(); + stack.push(1.into()).unwrap(); + stack.push(2.into()).unwrap(); + stack.swap_top(1).unwrap(); + assert_eq!(stack.pop().unwrap(), 1); + assert_eq!(stack.pop().unwrap(), 2); + + let mut stack = Stack::new(); + stack.push(1.into()).unwrap(); + stack.push(2.into()).unwrap(); + stack.push(3.into()).unwrap(); + stack.swap_top(2).unwrap(); + assert_eq!(stack.pop().unwrap(), 1); + assert_eq!(stack.pop().unwrap(), 2); + assert_eq!(stack.pop().unwrap(), 3); +} + +#[test] +fn test_swap_top_zero() { + let mut stack = Stack::new(); + stack.push(1.into()).unwrap(); + // do nothing + stack.swap_top(0).unwrap(); +} + +#[test] +fn test_stack_swap_underflow() { + let mut stack = Stack::new(); + assert_eq!(stack.swap_top(1).unwrap_err().exit_code(), EVM_CONTRACT_STACK_UNDERFLOW); + + stack.push(1.into()).unwrap(); + assert_eq!(stack.swap_top(1).unwrap_err().exit_code(), EVM_CONTRACT_STACK_UNDERFLOW); + + stack.push(2.into()).unwrap(); + assert_eq!(stack.swap_top(2).unwrap_err().exit_code(), EVM_CONTRACT_STACK_UNDERFLOW); +} + +#[test] +fn test_stack_dup() { + let mut stack = Stack::new(); + stack.push(1.into()).unwrap(); + stack.push(2.into()).unwrap(); + stack.dup(1).unwrap(); + assert_eq!(stack.pop().unwrap(), 2); + stack.dup(2).unwrap(); + assert_eq!(stack.pop().unwrap(), 1); + assert_eq!(stack.pop().unwrap(), 2); + assert_eq!(stack.pop().unwrap(), 1); +} + +#[test] +#[should_panic] +fn test_stack_dup_zero() { + let mut stack = Stack::new(); + stack.dup(0).unwrap(); +} + +#[test] +fn test_stack_dup_len() { + let mut stack = Stack::new(); + for i in 1..=6 { + stack.push(U256::from(i)).unwrap() + } + stack.dup(stack.len()).unwrap(); + let a = stack.pop(); + stack.pop_many::<5>().unwrap(); + assert_eq!(stack.pop(), a); + assert_eq!(a.unwrap(), U256::ONE); +} + +#[test] +fn test_stack_dup_underflow() { + let mut stack = Stack::new(); + assert_eq!(stack.dup(1).unwrap_err().exit_code(), EVM_CONTRACT_STACK_UNDERFLOW); + stack.push(1.into()).unwrap(); + assert_eq!(stack.dup(2).unwrap_err().exit_code(), EVM_CONTRACT_STACK_UNDERFLOW); +} + +#[test] +fn test_stack_overflow() { + let mut stack = Stack::new(); + for i in 0..1024 { + stack.push(i.into()).unwrap(); + } + + assert_eq!(stack.push(1024.into()).unwrap_err().exit_code(), EVM_CONTRACT_STACK_OVERFLOW); + assert_eq!(stack.dup(1).unwrap_err().exit_code(), EVM_CONTRACT_STACK_OVERFLOW); + stack.swap_top(1).unwrap(); + assert_eq!(stack.pop().unwrap(), 1022); +} diff --git a/actors/evm/src/interpreter/system.rs b/actors/evm/src/interpreter/system.rs new file mode 100644 index 000000000..aedc39d49 --- /dev/null +++ b/actors/evm/src/interpreter/system.rs @@ -0,0 +1,453 @@ +use std::borrow::Cow; + +use fil_actors_evm_shared::{address::EthAddress, uints::U256}; +use fil_actors_runtime::{ + actor_error, extract_send_result, runtime::EMPTY_ARR_CID, AsActorError, EAM_ACTOR_ID, +}; +use fvm_ipld_blockstore::Block; +use fvm_ipld_encoding::ipld_block::IpldBlock; +use fvm_ipld_encoding::CborStore; +use fvm_ipld_kamt::HashedKey; +use fvm_shared::{ + address::{Address, Payload}, + crypto::hash::SupportedHashes, + econ::TokenAmount, + error::{ErrorNumber, ExitCode}, + sys::SendFlags, + MethodNum, Response, IPLD_RAW, METHOD_SEND, +}; +use multihash::Code; + +use crate::state::{State, Tombstone}; +use crate::BytecodeHash; + +use { + cid::Cid, + fil_actors_runtime::{runtime::Runtime, ActorError}, + fvm_ipld_blockstore::Blockstore, + fvm_ipld_kamt::{AsHashedKey, Config as KamtConfig, Kamt}, +}; + +// The Solidity compiler creates contiguous array item keys. +// To prevent the tree from going very deep we use extensions, +// which the Kamt supports and does in all cases. +// +// There are maximum 32 levels in the tree with the default bit width of 8. +// The top few levels will have a higher level of overlap in their hashes. +// Intuitively these levels should be used for routing, not storing data. +// +// The only exception to this is the top level variables in the contract +// which solidity puts in the first few slots. There having to do extra +// lookups is burdensome, and they will always be accessed even for arrays +// because that's where the array length is stored. +// +// However, for Solidity, the size of the KV pairs is 2x256, which is +// comparable to a size of a CID pointer plus extension metadata. +// We can keep the root small either by force-pushing data down, +// or by not allowing many KV pairs in a slot. +// +// The following values have been set by looking at how the charts evolved +// with the test contract. They might not be the best for other contracts. +pub const KAMT_CONFIG: KamtConfig = KamtConfig { min_data_depth: 0, bit_width: 5, max_array_width: 1 }; + +pub struct StateHashAlgorithm; + +/// Wrapper around the base U256 type so we can control the byte order in the hash, because +/// the words backing `U256` are in little endian order, and we need them in big endian for +/// the nibbles to be co-located in the tree. +impl AsHashedKey for StateHashAlgorithm { + fn as_hashed_key(key: &U256) -> Cow> { + let mut bs = [0u8; 32]; + key.to_big_endian(&mut bs); + Cow::Owned(bs) + } +} + +/// The EVM stores its state as Key-Value pairs with both keys and values +/// being 256 bits long, which we store in a KAMT. +pub type StateKamt = Kamt; + +/// Maximum allowed EVM bytecode size. +/// The contract code size limit is 24kB. +const MAX_CODE_SIZE: usize = 24 << 10; + +#[derive(Clone, Copy)] +pub struct EvmBytecode { + /// CID of the contract + pub cid: Cid, + /// Keccak256 hash of the contract + pub evm_hash: BytecodeHash, +} + +impl EvmBytecode { + fn new(cid: Cid, evm_hash: BytecodeHash) -> Self { + Self { cid, evm_hash } + } +} + +/// Platform Abstraction Layer +/// that bridges the FVM world to EVM world +pub struct System<'r, RT: Runtime> { + pub rt: &'r mut RT, + + /// The current bytecode. This is usually only "none" when the actor is first constructed. + /// (blake2b256(ipld_raw(bytecode)), keccak256(bytecode)) + bytecode: Option, + /// The contract's EVM storage slots. + slots: StateKamt, + /// The contracts "nonce" (incremented when creating new actors). + pub(crate) nonce: u64, + /// The last saved state root. None if the current state hasn't been saved yet. + saved_state_root: Option, + /// Read Only context (staticcall) + pub readonly: bool, + /// Randomness taken from the current epoch of chain randomness + randomness: Option<[u8; 32]>, + + /// This is "some" if the actor is currently a "zombie". I.e., it has selfdestructed, but the + /// current message is still executing. `System` cannot load a contracts state with a + pub(crate) tombstone: Option, +} + +impl<'r, RT: Runtime> System<'r, RT> { + pub(crate) fn new(rt: &'r mut RT, readonly: bool) -> Self + where + RT::Blockstore: Clone, + { + let store = rt.store().clone(); + Self { + rt, + slots: StateKamt::new_with_config(store, KAMT_CONFIG.clone()), + nonce: 1, + saved_state_root: None, + bytecode: None, + readonly, + randomness: None, + tombstone: None, + } + } + + /// Resurrect the contract. This will return a new empty contract if, and only if, the contract + /// is "dead". + pub fn resurrect(rt: &'r mut RT) -> Result + where + RT::Blockstore: Clone, + { + let read_only = rt.read_only(); + let state_root = rt.get_state_root()?; + // Check the tombstone. + let state: State = rt + .store() + .get_cbor(&state_root) + .context_code(ExitCode::USR_SERIALIZATION, "failed to decode state")? + .context_code(ExitCode::USR_ILLEGAL_STATE, "state not in blockstore")?; + if !crate::is_dead(rt, &state) { + return Err(actor_error!(forbidden, "can only resurrect a dead contract")); + } + + return Ok(Self::new(rt, read_only)); + } + + /// Create the contract. This will return a new empty contract if, and only if, the contract + /// doesn't have any state. + pub fn create(rt: &'r mut RT) -> Result + where + RT::Blockstore: Clone, + { + let read_only = rt.read_only(); + let state_root = rt.get_state_root()?; + if state_root != EMPTY_ARR_CID { + return Err(actor_error!(illegal_state, "can't create over an existing actor")); + } + return Ok(Self::new(rt, read_only)); + } + + /// Load the actor from state. + pub fn load(rt: &'r mut RT) -> Result + where + RT::Blockstore: Clone, + { + let store = rt.store().clone(); + let state_root = rt.get_state_root()?; + let state: State = store + .get_cbor(&state_root) + .context_code(ExitCode::USR_SERIALIZATION, "failed to decode state")? + .context_code(ExitCode::USR_ILLEGAL_STATE, "state not in blockstore")?; + + if crate::is_dead(rt, &state) { + // If we're "dead", return an empty read-only contract. The code will be empty, so + // nothing can happen anyways. + return Ok(Self::new(rt, true)); + } + + let read_only = rt.read_only(); + + Ok(Self { + rt, + slots: StateKamt::load_with_config(&state.contract_state, store, KAMT_CONFIG.clone()) + .context_code(ExitCode::USR_ILLEGAL_STATE, "state not in blockstore")?, + nonce: state.nonce, + saved_state_root: Some(state_root), + bytecode: Some(EvmBytecode::new(state.bytecode, state.bytecode_hash)), + readonly: read_only, + randomness: None, + tombstone: state.tombstone, + }) + } + + pub fn increment_nonce(&mut self) -> u64 { + self.saved_state_root = None; + let nonce = self.nonce; + self.nonce = self.nonce.checked_add(1).unwrap(); + nonce + } + + /// Transfers funds to the receiver. This doesn't bother saving/reloading state. + pub fn transfer(&mut self, to: &Address, value: TokenAmount) -> Result<(), ActorError> { + extract_send_result(self.rt.send_simple(to, METHOD_SEND, None, value))?; + Ok(()) + } + + /// Generalized send + pub fn send( + &mut self, + to: &Address, + method: MethodNum, + params: Option, + value: TokenAmount, + gas_limit: Option, + send_flags: SendFlags, + ) -> Result, ActorError> { + let result = self.send_raw(to, method, params, value, gas_limit, send_flags)?.map_err(|err| { + actor_error!(unspecified; "send syscall to {to} on method {method} failed: {}", err) + })?; + + // Don't bother reloading on abort, just return the error. + if !result.exit_code.is_success() { + return Err(ActorError::checked( + result.exit_code, + format!("failed to call {to} on method {method}"), + result.return_data, + )); + } + + Ok(result.return_data) + } + + /// Send, but get back the raw syscall error failure without interpreting it as an actor error. + /// This method has a really funky return type because: + /// 1. It can fail with an outer "actor error". In that case, the EVM is expected to abort with + /// the specified exit code. + /// 2. It can fail with an inner syscall error. + /// 3. It can successfully call into the other actor, and return a response with a non-zero exit code. + pub fn send_raw( + &mut self, + to: &Address, + method: MethodNum, + params: Option, + value: TokenAmount, + gas_limit: Option, + send_flags: SendFlags, + ) -> Result, ActorError> { + self.flush()?; + let result = self.rt.send(to, method, params, value, gas_limit, send_flags); + + // Reload on success, and only on success. + match &result { + Ok(r) if r.exit_code.is_success() => self.reload()?, + _ => {} + } + + Ok(result.map_err(|e| e.0)) + } + + /// Flush the actor state (bytecode, nonce, and slots). + pub fn flush(&mut self) -> Result<(), ActorError> { + if self.saved_state_root.is_some() { + return Ok(()); + } + + if self.readonly { + return Err(ActorError::forbidden("contract invocation is read only".to_string())); + } + + let EvmBytecode { cid, evm_hash } = match self.bytecode { + Some(cid) => cid, + // set empty bytecode hashes + None => self.set_bytecode(&[])?, + }; + let new_root = self + .rt + .store() + .put_cbor( + &State { + bytecode: cid, + bytecode_hash: evm_hash, + contract_state: self.slots.flush().context_code( + ExitCode::USR_ILLEGAL_STATE, + "failed to flush contract state", + )?, + nonce: self.nonce, + tombstone: self.tombstone, + }, + Code::Blake2b256, + ) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to write contract state")?; + + self.rt.set_state_root(&new_root)?; + self.saved_state_root = Some(new_root); + Ok(()) + } + + /// Reload the actor state if changed. + pub fn reload(&mut self) -> Result<(), ActorError> { + if self.readonly { + return Ok(()); + } + + let root = self.rt.get_state_root()?; + if self.saved_state_root == Some(root) { + return Ok(()); + } + + let state: State = self + .rt + .store() + .get_cbor(&root) + .context_code(ExitCode::USR_SERIALIZATION, "failed to decode state")? + .context_code(ExitCode::USR_ILLEGAL_STATE, "state not in blockstore")?; + + self.slots + .set_root(&state.contract_state) + .context_code(ExitCode::USR_ILLEGAL_STATE, "state not in blockstore")?; + self.nonce = state.nonce; + self.saved_state_root = Some(root); + self.bytecode = Some(EvmBytecode::new(state.bytecode, state.bytecode_hash)); + Ok(()) + } + + /// Get the bytecode, if any. + pub fn get_bytecode(&self) -> Option { + self.bytecode.as_ref().map(|b| b.cid) + } + + /// Set the bytecode. + pub fn set_bytecode(&mut self, bytecode: &[u8]) -> Result { + self.saved_state_root = None; + if bytecode.len() > MAX_CODE_SIZE { + return Err(ActorError::illegal_argument(format!( + "EVM byte code length ({}) is exceeding the maximum allowed of {MAX_CODE_SIZE}", + bytecode.len() + ))); + } else if bytecode.first() == Some(&0xEF) { + // Reject code starting with 0xEF, EIP-3541 + return Err(ActorError::illegal_argument( + "EIP-3541: Contract code starting with the 0xEF byte is disallowed.".into(), + )); + } + + let code_hash = self.rt.hash(SupportedHashes::Keccak256, bytecode)[..] + .try_into() + .context_code(ExitCode::USR_ASSERTION_FAILED, "expected a 32byte digest")?; + + let cid = self + .rt + .store() + .put(Code::Blake2b256, &Block::new(IPLD_RAW, bytecode)) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to write bytecode")?; + let bytecode = EvmBytecode::new(cid, code_hash); + self.bytecode = Some(bytecode); + Ok(bytecode) + } + + /// Get value of a storage key. + pub fn get_storage(&mut self, key: U256) -> Result { + Ok(self + .slots + .get(&key) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to clear storage slot")? + .cloned() + .unwrap_or_default()) + } + + /// Set value of a storage key. + pub fn set_storage(&mut self, key: U256, value: U256) -> Result<(), ActorError> { + let changed = if value.is_zero() { + self.slots + .delete(&key) + .map(|v| v.is_some()) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to clear storage slot")? + } else { + self.slots + .set(key, value) + .map(|v| v != Some(value)) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to update storage slot")? + }; + + if changed { + self.saved_state_root = None; // dirty. + }; + Ok(()) + } + + /// Resolve the address to the ethereum equivalent, if possible. + /// + /// - Eth f4 maps directly to an Eth address. + /// - f3, f2, and f1, addresses will resolve to ID address then... + /// - Attempt to lookup Eth f4 address from ID address. + /// - Otherwise encode ID address into Eth address (0xff....\) + pub fn resolve_ethereum_address(&self, addr: &Address) -> Result { + // Short-circuit if we already have an EVM actor. + match addr.payload() { + Payload::Delegated(delegated) if delegated.namespace() == EAM_ACTOR_ID => { + let subaddr: [u8; 20] = delegated + .subaddress() + .try_into() + .with_context_code(ExitCode::USR_ILLEGAL_STATE, || { + format!("invalid ethereum address length: {addr}") + })?; + return Ok(EthAddress(subaddr)); + } + _ => {} + } + + // Otherwise, resolve to an ID address. + let actor_id = self.rt.resolve_address(addr).context_code( + ExitCode::USR_ILLEGAL_STATE, + "non-ethereum address {addr} cannot be resolved to an ID address", + )?; + + // Then attempt to resolve back into an EVM address. + match self.rt.lookup_delegated_address(actor_id).map(|a| a.into_payload()) { + Some(Payload::Delegated(delegated)) if delegated.namespace() == EAM_ACTOR_ID => { + let subaddr: [u8; 20] = delegated.subaddress().try_into().context_code( + ExitCode::USR_ILLEGAL_STATE, + "invalid ethereum address length: {addr}", + )?; + Ok(EthAddress(subaddr)) + } + // But use an EVM address as the fallback. + _ => Ok(EthAddress::from_id(actor_id)), + } + } + + /// Gets the cached EVM randomness seed of the current epoch + pub fn get_randomness(&mut self) -> Result<&[u8; 32], ActorError> { + const ENTROPY: &[u8] = b"prevrandao"; + match &mut self.randomness { + Some(rand) => Ok(&*rand), + // get randomness from current beacon epoch with entropy of "prevrandao" + cache => Ok(cache.insert(self.rt.get_randomness_from_beacon( + fil_actors_runtime::runtime::DomainSeparationTag::EvmPrevRandao, + self.rt.curr_epoch(), + ENTROPY, + )?)), + } + } + + /// Mark ourselves as "selfdestructed". + pub fn mark_selfdestructed(&mut self) { + self.saved_state_root = None; + self.tombstone = Some(crate::current_tombstone(self.rt)); + } +} diff --git a/actors/evm/src/interpreter/test_util.rs b/actors/evm/src/interpreter/test_util.rs new file mode 100644 index 000000000..6bdebb258 --- /dev/null +++ b/actors/evm/src/interpreter/test_util.rs @@ -0,0 +1,79 @@ +#[macro_export] +macro_rules! evm_instruction { + ($i:ident) => { + $crate::interpreter::execution::opcodes::$i + }; + ($i:literal) => { + $i + }; + ($i:expr) => { + $i + }; +} + +#[macro_export] +macro_rules! evm_unit_test { + (($rt:ident) $init:block ($machine:ident) { $($inst:tt;)* } $($body:tt)*) => { + use ::fil_actors_runtime::test_utils::MockRuntime; + use ::fvm_shared::econ::TokenAmount; + use $crate::interpreter::{execution::Machine, system::System, Output}; + use $crate::{Bytecode, EthAddress, ExecutionState}; + + let mut $rt = MockRuntime::default(); + $rt.in_call = true; + $init + + let mut state = ExecutionState::new( + EthAddress::from_id(1000), + EthAddress::from_id(1000), + TokenAmount::from_atto(0), + Vec::new(), + ); + + let code = vec![$($crate::evm_instruction!($inst)),*]; + + let mut system = System::new(&mut $rt, false); + let bytecode = Bytecode::new(code); + #[allow(unused_mut)] + let mut $machine = Machine { + system: &mut system, + state: &mut state, + bytecode: &bytecode, + pc: 0, + output: Output::default(), + }; + + $($body)* + }; + + (($machine:ident) { $($inst:tt;)* } $($body:tt)*) => { + use ::fil_actors_runtime::test_utils::MockRuntime; + use ::fvm_shared::econ::TokenAmount; + use $crate::interpreter::{execution::Machine, system::System, Output}; + use $crate::{Bytecode, EthAddress, ExecutionState}; + + let mut rt = MockRuntime::default(); + rt.in_call = true; + let mut state = ExecutionState::new( + EthAddress::from_id(1000), + EthAddress::from_id(1000), + TokenAmount::from_atto(0), + Vec::new(), + ); + + let code = vec![$($crate::evm_instruction!($inst)),*]; + + let mut system = System::new(&mut rt, false); + let bytecode = Bytecode::new(code); + #[allow(unused_mut)] + let mut $machine = Machine { + system: &mut system, + state: &mut state, + bytecode: &bytecode, + pc: 0, + output: Output::default(), + }; + + $($body)* + }; +} diff --git a/actors/evm/src/lib.rs b/actors/evm/src/lib.rs index 6f8b3dee2..4304c2acb 100644 --- a/actors/evm/src/lib.rs +++ b/actors/evm/src/lib.rs @@ -1 +1,486 @@ +use fil_actors_evm_shared::address::EthAddress; +use fil_actors_evm_shared::uints::U256; +use fil_actors_runtime::{actor_error, AsActorError, EAM_ACTOR_ADDR, INIT_ACTOR_ADDR}; +use fvm_ipld_blockstore::Blockstore; +use fvm_ipld_encoding::ipld_block::IpldBlock; +use fvm_ipld_encoding::{strict_bytes, BytesDe, BytesSer}; +use fvm_shared::address::Address; +use fvm_shared::econ::TokenAmount; +use fvm_shared::error::ExitCode; + +use crate::interpreter::Outcome; +use crate::reader::ValueReader; + +pub use types::*; + +#[doc(hidden)] +pub mod ext; +pub mod interpreter; +pub(crate) mod reader; +mod state; mod types; + +use { + crate::interpreter::{execute, Bytecode, ExecutionState, System}, + cid::Cid, + fil_actors_runtime::{ + runtime::{ActorCode, Runtime}, + ActorError, + }, + fvm_ipld_encoding::tuple::*, + fvm_shared::{MethodNum, METHOD_CONSTRUCTOR}, + num_derive::FromPrimitive, + num_traits::FromPrimitive, +}; + +pub use state::*; + +#[cfg(feature = "fil-actor")] +fil_actors_runtime::wasm_trampoline!(EvmContractActor); + +pub const EVM_CONTRACT_REVERTED: ExitCode = ExitCode::new(33); +pub const EVM_CONTRACT_INVALID_INSTRUCTION: ExitCode = ExitCode::new(34); +pub const EVM_CONTRACT_UNDEFINED_INSTRUCTION: ExitCode = ExitCode::new(35); +pub const EVM_CONTRACT_STACK_UNDERFLOW: ExitCode = ExitCode::new(36); +pub const EVM_CONTRACT_STACK_OVERFLOW: ExitCode = ExitCode::new(37); +pub const EVM_CONTRACT_ILLEGAL_MEMORY_ACCESS: ExitCode = ExitCode::new(38); +pub const EVM_CONTRACT_BAD_JUMPDEST: ExitCode = ExitCode::new(39); +pub const EVM_CONTRACT_SELFDESTRUCT_FAILED: ExitCode = ExitCode::new(40); + +const EVM_MAX_RESERVED_METHOD: u64 = 1023; +pub const NATIVE_METHOD_SIGNATURE: &str = "handle_filecoin_method(uint64,uint64,bytes)"; +pub const NATIVE_METHOD_SELECTOR: [u8; 4] = [0x86, 0x8e, 0x10, 0xc4]; + +const EVM_WORD_SIZE: usize = 32; + +#[test] +fn test_method_selector() { + // We could just _generate_ this method selector with a proc macro, but this is easier. + use cid::multihash::MultihashDigest; + let hash = cid::multihash::Code::Keccak256.digest(NATIVE_METHOD_SIGNATURE.as_bytes()); + let computed_selector = &hash.digest()[..4]; + assert_eq!(computed_selector, NATIVE_METHOD_SELECTOR); +} + +#[derive(FromPrimitive)] +#[repr(u64)] +pub enum Method { + Constructor = METHOD_CONSTRUCTOR, + Resurrect = 2, + GetBytecode = 3, + GetBytecodeHash = 4, + GetStorageAt = 5, + InvokeContractDelegate = 6, + InvokeContract = frc42_dispatch::method_hash!("InvokeEVM"), +} + +pub struct EvmContractActor; + +/// Returns a tombstone for the currently executing message. +pub(crate) fn current_tombstone(rt: &impl Runtime) -> Tombstone { + Tombstone { origin: rt.message().origin().id().unwrap(), nonce: rt.message().nonce() } +} + +/// Returns true if the contract is "dead". A contract is dead if: +/// +/// 1. It has a tombstone. +/// 2. It's tombstone is not from the current message execution (the nonce/origin don't match the +/// currently executing message). +/// +/// Specifically, this lets us mark the contract as "self-destructed" but keep it alive until the +/// current top-level message finishes executing. +pub(crate) fn is_dead(rt: &impl Runtime, state: &State) -> bool { + state.tombstone.map_or(false, |t| t != current_tombstone(rt)) +} + +fn load_bytecode(bs: &impl Blockstore, cid: &Cid) -> Result, ActorError> { + let bytecode = bs + .get(cid) + .context_code(ExitCode::USR_NOT_FOUND, "failed to read bytecode")? + .expect("bytecode not in state tree"); + if bytecode.is_empty() { + Ok(None) + } else { + Ok(Some(Bytecode::new(bytecode))) + } +} + +fn initialize_evm_contract( + system: &mut System, + caller: EthAddress, + initcode: Vec, +) -> Result<(), ActorError> { + // Lookup our Ethereum address. + let receiver_fil_addr = system.rt.message().receiver(); + let receiver_eth_addr = system.resolve_ethereum_address(&receiver_fil_addr).context_code( + ExitCode::USR_ASSERTION_FAILED, + "failed to resolve the contracts ETH address", + )?; + + // Make sure we have an actual Ethereum address (assigned by the EAM). This is how we make sure + // an EVM actor may only be constructed by the EAM. + if receiver_eth_addr.as_id().is_some() { + return Err(ActorError::forbidden(format!( + "contract {} doesn't have an eth address", + receiver_fil_addr, + ))); + } + + // If we have no code, save the state and return. + if initcode.is_empty() { + return system.flush(); + } + + // create a new execution context + let value_received = system.rt.message().value_received(); + let mut exec_state = ExecutionState::new(caller, receiver_eth_addr, value_received, Vec::new()); + + // identify bytecode valid jump destinations + let initcode = Bytecode::new(initcode); + + // invoke the contract constructor + let output = execute(&initcode, &mut exec_state, system)?; + + match output.outcome { + Outcome::Return => { + system.set_bytecode(&output.return_data)?; + system.flush() + } + Outcome::Revert => Err(ActorError::unchecked_with_data( + EVM_CONTRACT_REVERTED, + "constructor reverted".to_string(), + IpldBlock::serialize_cbor(&BytesSer(&output.return_data)).unwrap(), + )), + } +} + +fn invoke_contract_inner( + system: &mut System, + input_data: Vec, + bytecode_cid: &Cid, + caller: &EthAddress, + value_received: TokenAmount, +) -> Result, ActorError> +where + RT: Runtime, + RT::Blockstore: Clone, +{ + let bytecode = match load_bytecode(system.rt.store(), bytecode_cid)? { + Some(bytecode) => bytecode, + // an EVM contract with no code returns immediately + None => return Ok(Vec::new()), + }; + + // Resolve the receiver's ethereum address. + let receiver_fil_addr = system.rt.message().receiver(); + let receiver_eth_addr = system.resolve_ethereum_address(&receiver_fil_addr).unwrap(); + + let mut exec_state = + ExecutionState::new(*caller, receiver_eth_addr, value_received, input_data); + + let output = execute(&bytecode, &mut exec_state, system)?; + + match output.outcome { + Outcome::Return => { + system.flush()?; + Ok(output.return_data.to_vec()) + } + Outcome::Revert => Err(ActorError::unchecked_with_data( + EVM_CONTRACT_REVERTED, + "contract reverted".to_string(), + IpldBlock::serialize_cbor(&BytesSer(&output.return_data)).unwrap(), + )), + } +} + +impl EvmContractActor { + pub fn constructor(rt: &mut RT, params: ConstructorParams) -> Result<(), ActorError> + where + RT: Runtime, + RT::Blockstore: Clone, + { + rt.validate_immediate_caller_is(&[INIT_ACTOR_ADDR])?; + initialize_evm_contract(&mut System::create(rt)?, params.creator, params.initcode.into()) + } + + pub fn resurrect(rt: &mut RT, params: ResurrectParams) -> Result<(), ActorError> + where + RT: Runtime, + RT::Blockstore: Clone, + { + rt.validate_immediate_caller_is(&[EAM_ACTOR_ADDR])?; + initialize_evm_contract(&mut System::resurrect(rt)?, params.creator, params.initcode.into()) + } + + pub fn invoke_contract_delegate( + rt: &mut RT, + params: DelegateCallParams, + ) -> Result, ActorError> + where + RT: Runtime, + RT::Blockstore: Clone, + { + rt.validate_immediate_caller_is(&[rt.message().receiver()])?; + + let mut system = System::load(rt).map_err(|e| { + ActorError::unspecified(format!("failed to create execution abstraction layer: {e:?}")) + })?; + invoke_contract_inner(&mut system, params.input, ¶ms.code, ¶ms.caller, params.value) + } + + pub fn invoke_contract(rt: &mut RT, input_data: Vec) -> Result, ActorError> + where + RT: Runtime, + RT::Blockstore: Clone, + { + rt.validate_immediate_caller_accept_any()?; + + let mut system = System::load(rt).map_err(|e| { + ActorError::unspecified(format!("failed to create execution abstraction layer: {e:?}")) + })?; + + let bytecode_cid = match system.get_bytecode() { + Some(bytecode_cid) => bytecode_cid, + // an EVM contract with no code returns immediately + None => return Ok(Vec::new()), + }; + + let received_value = system.rt.message().value_received(); + let caller = system.resolve_ethereum_address(&system.rt.message().caller()).unwrap(); + invoke_contract_inner(&mut system, input_data, &bytecode_cid, &caller, received_value) + } + + pub fn handle_filecoin_method( + rt: &mut RT, + method: u64, + args: Option, + ) -> Result, ActorError> + where + RT: Runtime, + RT::Blockstore: Clone, + { + let params = args.unwrap_or(IpldBlock { codec: 0, data: vec![] }); + let input = handle_filecoin_method_input(method, params.codec, params.data.as_slice()); + let output = Self::invoke_contract(rt, input)?; + handle_filecoin_method_output(&output) + } + + /// Returns the contract's EVM bytecode, or `None` if the contract has been deleted (has called + /// SELFDESTRUCT). + pub fn bytecode(rt: &mut impl Runtime) -> Result, ActorError> { + // Any caller can fetch the bytecode of a contract; this is now EXT* opcodes work. + rt.validate_immediate_caller_accept_any()?; + + let state: State = rt.state()?; + if is_dead(rt, &state) { + Ok(None) + } else { + Ok(Some(state.bytecode)) + } + } + + pub fn bytecode_hash(rt: &mut impl Runtime) -> Result { + // Any caller can fetch the bytecode hash of a contract; this is where EXTCODEHASH gets it's value for EVM contracts. + rt.validate_immediate_caller_accept_any()?; + + // return value must be either keccak("") or keccak(bytecode) + let state: State = rt.state()?; + if is_dead(rt, &state) { + Ok(BytecodeHash::EMPTY) + } else { + Ok(state.bytecode_hash) + } + } + + pub fn storage_at(rt: &mut RT, params: GetStorageAtParams) -> Result + where + RT: Runtime, + RT::Blockstore: Clone, + { + // This method cannot be called on-chain; other on-chain logic should not be able to + // access arbitrary storage keys from a contract. + rt.validate_immediate_caller_is([&Address::new_id(0)])?; + + // If the contract is dead, this will always return "0". + System::load(rt)? + .get_storage(params.storage_key) + .context_code(ExitCode::USR_ASSERTION_FAILED, "failed to get storage key") + } +} + +/// Format "filecoin_native_method" input parameters. +fn handle_filecoin_method_input(method: u64, codec: u64, params: &[u8]) -> Vec { + let static_args = + [method, codec, EVM_WORD_SIZE as u64 * 3 /* start of params */, params.len() as u64]; + let total_words = + static_args.len() + (params.len() / EVM_WORD_SIZE) + (params.len() % 32 > 0) as usize; + let len = 4 + total_words * EVM_WORD_SIZE; + let mut buf = Vec::with_capacity(len); + buf.extend_from_slice(&NATIVE_METHOD_SELECTOR); + for n in static_args { + // Left-pad to 32 bytes, then be-encode the value. + let encoded = n.to_be_bytes(); + buf.resize(buf.len() + (EVM_WORD_SIZE - encoded.len()), 0); + buf.extend_from_slice(&encoded); + } + // Extend with the params, then right-pad with zeros. + buf.extend_from_slice(params); + buf.resize(len, 0); + buf +} + +/// Decode the response from "filecoin_native_method". We expect: +/// +/// 1. The exit code (u32). +/// 2. The codec (u64). +/// 3. The data (bytes). +/// +/// According to the solidity ABI. +fn handle_filecoin_method_output(output: &[u8]) -> Result, ActorError> { + // Short-circuit if empty. + if output.is_empty() { + return Ok(None); + } + let mut output = ValueReader::new(output); + + let exit_code: ExitCode = + output.read_value().context_code(ExitCode::USR_SERIALIZATION, "exit code not a u32")?; + let codec: u64 = output + .read_value() + .context_code(ExitCode::USR_SERIALIZATION, "returned codec not a u64")?; + let len_offset: u32 = output + .read_value() + .context_code(ExitCode::USR_SERIALIZATION, "invalid return value offset")?; + + output.seek(len_offset as usize); + let length: u32 = output + .read_value() + .context_code(ExitCode::USR_SERIALIZATION, "return length is too large")?; + let return_data = output.read_padded(length as usize); + + let return_block = match codec { + // Empty return values. + 0 if length == 0 => None, + 0 => { + return Err(ActorError::serialization(format!( + "codec 0 is only valid for empty returns, got a return value of length {length}" + ))); + } + // Supported codecs. + fvm_ipld_encoding::CBOR => Some(IpldBlock { codec, data: return_data.into() }), + #[cfg(feature = "hyperspace")] + fvm_ipld_encoding::DAG_CBOR => Some(IpldBlock { codec, data: return_data.into() }), + // Everything else. + _ => return Err(ActorError::serialization(format!("unsupported codec: {codec}"))), + }; + + if exit_code.is_success() { + Ok(return_block) + } else { + Err(ActorError::unchecked_with_data( + exit_code, + "EVM contract explicitly exited with a non-zero exit code".to_string(), + return_block, + )) + } +} + +impl ActorCode for EvmContractActor { + type Methods = Method; + // TODO: Use actor_dispatch macros for this: https://github.com/filecoin-project/builtin-actors/issues/966 + fn invoke_method( + rt: &mut RT, + method: MethodNum, + args: Option, + ) -> Result, ActorError> + where + RT: Runtime, + RT::Blockstore: Clone, + { + match FromPrimitive::from_u64(method) { + Some(Method::Constructor) => { + Self::constructor( + rt, + args.with_context_code(ExitCode::USR_ILLEGAL_ARGUMENT, || { + "method expects arguments".to_string() + })? + .deserialize()?, + )?; + Ok(None) + } + Some(Method::InvokeContract) => { + let params = match args { + None => vec![], + Some(p) => { + let BytesDe(p) = p.deserialize()?; + p + } + }; + let value = Self::invoke_contract(rt, params)?; + Ok(IpldBlock::serialize_cbor(&BytesSer(&value))?) + } + Some(Method::GetBytecode) => { + let ret = Self::bytecode(rt)?; + Ok(IpldBlock::serialize_dag_cbor(&ret)?) + } + Some(Method::GetBytecodeHash) => { + let hash = Self::bytecode_hash(rt)?; + Ok(IpldBlock::serialize_cbor(&hash)?) + } + Some(Method::GetStorageAt) => { + let value = Self::storage_at( + rt, + args.with_context_code(ExitCode::USR_ILLEGAL_ARGUMENT, || { + "method expects arguments".to_string() + })? + .deserialize()?, + )?; + Ok(IpldBlock::serialize_cbor(&value)?) + } + Some(Method::InvokeContractDelegate) => { + let params: DelegateCallParams = args + .with_context_code(ExitCode::USR_ILLEGAL_ARGUMENT, || { + "method expects arguments".to_string() + })? + .deserialize()?; + let value = Self::invoke_contract_delegate(rt, params)?; + Ok(IpldBlock::serialize_cbor(&BytesSer(&value))?) + } + Some(Method::Resurrect) => { + Self::resurrect( + rt, + args.with_context_code(ExitCode::USR_ILLEGAL_ARGUMENT, || { + "method expects arguments".to_string() + })? + .deserialize()?, + )?; + Ok(None) + } + None if method > EVM_MAX_RESERVED_METHOD => { + // We reserve all methods below EVM_MAX_RESERVED (<= 1023) method. This is a + // _subset_ of those reserved by FRC0042. + Self::handle_filecoin_method(rt, method, args) + } + None => Err(actor_error!(unhandled_message; "Invalid method")), + } + } +} + +pub type ResurrectParams = ConstructorParams; + +#[derive(Serialize_tuple, Deserialize_tuple)] +pub struct DelegateCallParams { + pub code: Cid, + /// The contract invocation parameters + #[serde(with = "strict_bytes")] + pub input: Vec, + /// The original caller's Eth address. + pub caller: EthAddress, + /// The value passed in the original call. + pub value: TokenAmount, +} + +#[derive(Serialize_tuple, Deserialize_tuple)] +pub struct GetStorageAtParams { + pub storage_key: U256, +} diff --git a/actors/evm/src/reader.rs b/actors/evm/src/reader.rs new file mode 100644 index 000000000..b7ca1a246 --- /dev/null +++ b/actors/evm/src/reader.rs @@ -0,0 +1,254 @@ +use std::{borrow::Cow, fmt::Display}; + +use fil_actors_evm_shared::uints::U256; +use fvm_shared::{bigint::BigUint, error::ExitCode}; +use substrate_bn::{AffineG1, CurveError, FieldError, Fq, Fr, Group, G1}; + +#[derive(Clone, Eq, PartialEq, Debug)] +pub struct OverflowError; + +impl Display for OverflowError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("value overflowed") + } +} + +/// A `Value` is a type that can be read from a `ValueReader`. These values are usually used in +/// solidity inputs/outputs. +pub(crate) trait Value: Sized { + type Error; + fn read(reader: &mut ValueReader) -> Result; +} + +impl Value for G1 { + type Error = CurveError; + fn read(reader: &mut ValueReader) -> Result { + let x: Fq = reader.read_value()?; + let y: Fq = reader.read_value()?; + + Ok(if x.is_zero() && y.is_zero() { + G1::zero() + } else { + AffineG1::new(x, y).map_err(|_| CurveError::NotMember)?.into() + }) + } +} + +impl Value for Fq { + type Error = FieldError; + fn read(reader: &mut ValueReader) -> Result { + Fq::from_slice(&reader.read_fixed::<32>()) + } +} + +impl Value for Fr { + type Error = FieldError; + fn read(reader: &mut ValueReader) -> Result { + Fr::from_slice(&reader.read_fixed::<32>()) + } +} + +impl Value for ExitCode { + type Error = OverflowError; + + fn read(reader: &mut ValueReader) -> Result { + Ok(ExitCode::new(reader.read_value()?)) + } +} + +impl Value for U256 { + type Error = FieldError; + fn read(reader: &mut ValueReader) -> Result { + Ok(U256::from(reader.read_fixed::<32>())) + } +} + +impl Value for [u8; 32] { + type Error = std::convert::Infallible; + fn read(reader: &mut ValueReader) -> Result { + Ok(reader.read_fixed()) + } +} + +impl Value for u8 { + type Error = OverflowError; + fn read(reader: &mut ValueReader) -> Result { + reader.drop_zeros::<31>()?; + Ok(reader.read_byte()) + } +} + +macro_rules! impl_value_int { + ($($t:ty)*) => { + $( + impl Value for $t { + type Error = OverflowError; + fn read(reader: &mut ValueReader) -> Result { + const ZEROS: usize = 32 - ((<$t>::BITS as usize) / 8); + + reader.drop_zeros::()?; + // Type ensures our remaining len + Ok(<$t>::from_be_bytes(reader.read_fixed())) + } + } + )* + }; +} + +impl_value_int!(u16 i16 u32 i32 u64 i64); + +/// Provides a nice API interface for reading Values from input. This API treats the input as if it +/// is followed by infinite zeros. +pub(crate) struct ValueReader<'a> { + full: &'a [u8], + slice: &'a [u8], +} + +impl<'a> ValueReader<'a> { + pub fn new(slice: &'a [u8]) -> Self { + ValueReader { full: slice, slice } + } + + /// Seek to an offset from the beginning of the input. + pub fn seek(&mut self, offset: usize) { + if offset > self.full.len() { + self.slice = &[]; + } else { + self.slice = &self.full[offset..]; + } + } + + /// Drop a fixed number of bytes, and return an error if said bytes are not zeros. + pub fn drop_zeros(&mut self) -> Result<(), OverflowError> { + let split = S.min(self.slice.len()); + let (a, b) = self.slice.split_at(split); + self.slice = b; + if a.iter().all(|&i| i == 0) { + Ok(()) + } else { + Err(OverflowError) + } + } + + /// Read a single byte, or 0 if there's no remaining input. + /// + /// NOTE: This won't read 32 bytes, it'll just read a _single_ byte. + pub fn read_byte(&mut self) -> u8 { + if let Some((&first, rest)) = self.slice.split_first() { + self.slice = rest; + first + } else { + 0 + } + } + + /// Read a fixed number of bytes from the input, zero-padding as necessary. + /// + /// NOTE: this won't read in 32byte chunks, it'll read the specified number of bytes exactly. + pub fn read_fixed(&mut self) -> [u8; S] { + let mut out = [0u8; S]; + let split = S.min(self.slice.len()); + let (a, b) = self.slice.split_at(split); + self.slice = b; + out[..split].copy_from_slice(a); + out + } + + /// Read input and pad up to `len`. + pub fn read_padded(&mut self, len: usize) -> Cow<'a, [u8]> { + if len <= self.slice.len() { + let (a, b) = self.slice.split_at(len); + self.slice = b; + Cow::Borrowed(a) + } else { + let mut buf = Vec::with_capacity(len); + buf.extend_from_slice(self.slice); + buf.resize(len, 0); + self.slice = &[]; + Cow::Owned(buf) + } + } + + /// Read a bigint from the input, and pad up to `len`. + pub fn read_biguint(&mut self, len: usize) -> BigUint { + // We read the bigint in two steps: + // 1. We read any bytes that are actually present in the input. + // 2. Then we pad by _shifting_ the integer by the number of missing bits. + let split = len.min(self.slice.len()); + let (a, b) = self.slice.split_at(split); + self.slice = b; + + // Start with the existing bytes. + let mut int = BigUint::from_bytes_be(a); + // Then shift, if necessary. + if split < len { + int <<= ((len - split) as u32) * u8::BITS; + } + int + } + + /// Read a single value from the input. The value's type decides how much input it needs + /// to read. + /// + /// Most values will be read in 32 byte chunks, but that's up to the value's implementation. + pub fn read_value(&mut self) -> Result + where + V: Value, + { + Value::read(self) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_read_fixed() { + let mut reader = ValueReader::new(&[1, 2, 3]); + let empty: [u8; 0] = []; + assert_eq!(reader.read_fixed::<2>(), [1, 2]); + assert_eq!(reader.read_fixed::<0>(), empty); + assert_eq!(reader.read_fixed::<5>(), [3u8, 0, 0, 0, 0]); + assert_eq!(reader.read_fixed::<3>(), [0, 0, 0]); + } + + #[test] + fn test_right_pad() { + let mut reader = ValueReader::new(&[1, 2, 3]); + assert_eq!(reader.read_padded(2), &[1, 2][..]); + assert_eq!(reader.read_padded(2), &[3, 0][..]); + assert_eq!(reader.read_padded(2), &[0, 0][..]); + } + + #[test] + fn test_int() { + let mut data = vec![0u8; 37]; + data[31] = 1; + let mut reader = ValueReader::new(&data); + assert_eq!(reader.read_value::().unwrap(), 1); + assert_eq!(reader.read_value::().unwrap(), 0); + + // Expect this to overflow now. + data[0] = 1; + let mut reader = ValueReader::new(&data); + assert_eq!(reader.read_value::().unwrap_err(), OverflowError); + } + + #[test] + fn test_big_int() { + let mut reader = ValueReader::new(&[1, 2]); + assert_eq!(reader.read_biguint(1), 1u64.into()); + assert_eq!(reader.read_biguint(3), 0x02_00_00u64.into()); + assert_eq!(reader.read_biguint(5), 0u32.into()); + } + + #[test] + fn test_byte() { + let mut reader = ValueReader::new(&[1, 2]); + assert_eq!(reader.read_byte(), 1u8); + assert_eq!(reader.read_byte(), 2u8); + assert_eq!(reader.read_byte(), 0u8); + assert_eq!(reader.read_byte(), 0u8); + } +} diff --git a/actors/evm/src/state.rs b/actors/evm/src/state.rs new file mode 100644 index 000000000..ae5d6892f --- /dev/null +++ b/actors/evm/src/state.rs @@ -0,0 +1,163 @@ +use std::array::TryFromSliceError; + +use fil_actors_evm_shared::uints::U256; +use fvm_shared::ActorID; + +use { + cid::Cid, + fvm_ipld_encoding::strict_bytes, + fvm_ipld_encoding::tuple::*, + serde::{Deserialize, Serialize}, + serde_tuple::{Deserialize_tuple, Serialize_tuple}, +}; + +/// A tombstone indicating that the contract has been self-destructed. +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize_tuple, Deserialize_tuple)] +pub struct Tombstone { + /// The message origin when this actor was self-destructed. + pub origin: ActorID, + /// The message nonce when this actor was self-destructed. + pub nonce: u64, +} + +/// A Keccak256 digest of EVM bytecode. +#[derive(Deserialize, Serialize, Clone, Copy, Eq, PartialEq)] +#[serde(transparent)] +pub struct BytecodeHash(#[serde(with = "strict_bytes")] [u8; 32]); + +impl std::fmt::Debug for BytecodeHash { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("BytecodeHash").field(&format_args!("{}", self)).finish() + } +} + +impl std::fmt::Display for BytecodeHash { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if f.alternate() { + write!(f, "0x")?; + } + for b in self.0 { + write!(f, "{b:02X}")?; + } + Ok(()) + } +} + +impl BytecodeHash { + pub const ZERO: Self = Self([0; 32]); + + /// Keccak256 hash of `[0xfe]`, "native bytecode" + pub const NATIVE_ACTOR: Self = + Self(hex_literal::hex!("bcc90f2d6dada5b18e155c17a1c0a55920aae94f39857d39d0d8ed07ae8f228b")); + + /// Keccak256 hash of `[]`, empty bytecode + pub const EMPTY: Self = + Self(hex_literal::hex!("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")); + + pub fn as_slice(&self) -> &[u8] { + &self.0 + } +} + +impl From<[u8; 32]> for BytecodeHash { + fn from(digest: [u8; 32]) -> Self { + BytecodeHash(digest) + } +} + +impl From for [u8; 32] { + fn from(digest: BytecodeHash) -> Self { + digest.0 + } +} + +impl From for Vec { + fn from(digest: BytecodeHash) -> Self { + digest.0.into() + } +} + +impl From for U256 { + fn from(bytecode: BytecodeHash) -> Self { + let bytes: [u8; 32] = bytecode.into(); + Self::from(bytes) + } +} + +impl TryFrom<&[u8]> for BytecodeHash { + type Error = TryFromSliceError; + + fn try_from(value: &[u8]) -> Result { + Ok(Self(value.try_into()?)) + } +} + +/// Data stored by an EVM contract. +/// This runs on the fvm-evm-runtime actor code cid. +#[derive(Debug, Serialize_tuple, Deserialize_tuple)] +pub struct State { + /// The EVM contract bytecode resulting from calling the + /// initialization code by the constructor. + pub bytecode: Cid, + + /// The EVM contract bytecode hash keccak256(bytecode) + pub bytecode_hash: BytecodeHash, + + /// The EVM contract state dictionary. + /// All eth contract state is a map of U256 -> U256 values. + /// + /// KAMT + pub contract_state: Cid, + + /// The EVM nonce used to track how many times CREATE or CREATE2 have been called. + pub nonce: u64, + + /// Possibly a tombstone if this actor has been self-destructed. + /// + /// In the EVM, self-destructed contracts are "alive" until the current top-level transaction + /// ends. We track this by recording the origin and nonce. + /// + /// Specifically: + /// + /// 1. On SELFDESTRUCT, they mark themselves as "deleted" (by setting a tombstone with the + /// current origin/nonce), send away all funds, and return immediately. + /// 2. For the rest of the current transaction (as long as the tombstone's origin/nonce matches + /// the currently executing top-level transaction) , the contract continues to behave + /// normally. + /// 3. After the current transaction ends, the contract behaves as if it were an "empty" + /// contract, kind of like an embryo. At this point, the contract can be "resurrected" + /// (recreated) by via CREATE/CREATE2. + /// + /// See https://github.com/filecoin-project/ref-fvm/issues/1174 for some context. + pub tombstone: Option, +} + +#[cfg(test)] +mod test { + use fvm_ipld_encoding::{from_slice, to_vec, BytesDe}; + + use crate::BytecodeHash; + #[test] + fn test_bytecode_hash_serde() { + let encoded = to_vec(&BytecodeHash::EMPTY).unwrap(); + let BytesDe(decoded) = from_slice(&encoded).unwrap(); + assert_eq!(BytecodeHash::try_from(&decoded[..]).unwrap(), BytecodeHash::EMPTY); + } + + #[test] + fn test_bytecode_hash_format() { + assert_eq!( + BytecodeHash::ZERO.to_string(), + "0000000000000000000000000000000000000000000000000000000000000000" + ); + assert_eq!( + format!("{:#}", BytecodeHash::ZERO), + "0x0000000000000000000000000000000000000000000000000000000000000000" + ); + + assert_eq!( + format!("{:?}", BytecodeHash::ZERO), + "BytecodeHash(0000000000000000000000000000000000000000000000000000000000000000)" + ); + } +} diff --git a/actors/evm/tests/asm.rs b/actors/evm/tests/asm.rs new file mode 100644 index 000000000..db2e08192 --- /dev/null +++ b/actors/evm/tests/asm.rs @@ -0,0 +1,93 @@ +use etk_asm::ingest::Ingest; +use evm::interpreter::opcodes; +use fil_actor_evm as evm; + +const PRELUDE: &str = r#" +%macro dispatch_begin() + push1 0x00 + calldataload + push1 0xe0 # 28 byte shift == 224 bits + shr +%end + +%macro dispatch(method, lbl) + dup1 + %push($method) + eq + %push($lbl) + jumpi +%end + +%macro dispatch_end() + push1 0x00 + dup1 + revert +%end + +%macro return_stack_word() + # store at 0x00 + push1 0x00 + mstore + push1 0x20 # always return a full word + push1 0x00 + return +%end +"#; + +#[allow(dead_code)] +/// Creates a new EVM contract constructon bytecode (initcode), suitable for initializing the EVM actor. +/// Arguments: +/// - name is the name of the contract, for debug purposes. +/// - init is the initializer code, which will run first at contract construction. +/// - body is the actual contract code. +pub fn new_contract(name: &str, init: &str, body: &str) -> Result, etk_asm::ingest::Error> { + // the contract code + let mut body_code = Vec::new(); + let mut ingest_body = Ingest::new(&mut body_code); + let body_with_prelude = PRELUDE.to_owned() + body; + ingest_body.ingest(name, body_with_prelude.as_str())?; + // the initialization code + let mut init_code = Vec::new(); + let mut ingest_init = Ingest::new(&mut init_code); + ingest_init.ingest(name, init)?; + // synthesize contract constructor + let body_code_len = body_code.len(); + let body_code_offset = init_code.len() + + 1 // PUSH4 + + 4 // 4-bytes for code length + + 1 // DUP1 + + 1 // PUSH4 + + 4 // 4 bytes for the code offset itself + + 1 // PUSH1 + + 1 // 0x00 -- destination memory offset + + 1 // CODECOPY + + 1 // PUSH1 + + 1 // 0x00 -- source memory offset + + 1 // RETURN + ; + let mut constructor_code = vec![ + opcodes::PUSH4, + ((body_code_len >> 24) & 0xff) as u8, + ((body_code_len >> 16) & 0xff) as u8, + ((body_code_len >> 8) & 0xff) as u8, + (body_code_len & 0xff) as u8, + opcodes::DUP1, + opcodes::PUSH4, + ((body_code_offset >> 24) & 0xff) as u8, + ((body_code_offset >> 16) & 0xff) as u8, + ((body_code_offset >> 8) & 0xff) as u8, + (body_code_offset & 0xff) as u8, + opcodes::PUSH1, + 0x00, + opcodes::CODECOPY, + opcodes::PUSH1, + 0x00, + opcodes::RETURN, + ]; + // the actual contract code + let mut contract_code = Vec::new(); + contract_code.append(&mut init_code); + contract_code.append(&mut constructor_code); + contract_code.append(&mut body_code); + Ok(contract_code) +} diff --git a/actors/evm/tests/basic.rs b/actors/evm/tests/basic.rs new file mode 100644 index 000000000..c9c592b5c --- /dev/null +++ b/actors/evm/tests/basic.rs @@ -0,0 +1,204 @@ +mod asm; + +use cid::Cid; +use fil_actor_evm as evm; +use fil_actors_evm_shared::uints::U256; +use fil_actors_runtime::test_utils::*; +use fvm_ipld_blockstore::Blockstore; +use fvm_ipld_encoding::ipld_block::IpldBlock; +use fvm_shared::address::Address; + +mod util; + +#[test] +fn basic_contract_construction_and_invocation_fe_lang() { + let bytecode = + hex::decode(include_str!("contracts/output/FeSimplecoin/FeSimplecoin.bin")).unwrap(); + simplecoin_test(bytecode); +} + +#[test] +fn basic_contract_construction_and_invocation() { + let bytecode = hex::decode(include_str!("contracts/simplecoin.hex")).unwrap(); + simplecoin_test(bytecode); +} + +fn simplecoin_test(bytecode: Vec) { + let contract = Address::new_id(100); + + let mut rt = util::init_construct_and_verify(bytecode, |rt| { + rt.actor_code_cids.insert(contract, *EVM_ACTOR_CODE_ID); + rt.set_origin(contract); + }); + + // invoke contract -- getBalance + // first we invoke without specifying an address, so it would be the system actor and have + // a balance of 0 + + let mut solidity_params = vec![]; + solidity_params.append(&mut hex::decode("f8b2cb4f").unwrap()); // function selector + // caller id address in U256 form + let mut arg0 = vec![0u8; 32]; + solidity_params.append(&mut arg0); + + let result = util::invoke_contract(&mut rt, &solidity_params); + assert_eq!(U256::from_big_endian(&result), U256::from(0)); + + // invoke contract -- getBalance + // now we invoke with the owner address, which should have a balance of 10k + let mut solidity_params = vec![]; + solidity_params.append(&mut hex::decode("f8b2cb4f").unwrap()); // function selector + // caller id address in U256 form + let mut arg0 = vec![0u8; 32]; + arg0[12] = 0xff; // it's an ID address, so we enable the flag + arg0[31] = 100; // the owner address + solidity_params.append(&mut arg0); + + let result = util::invoke_contract(&mut rt, &solidity_params); + assert_eq!(U256::from_big_endian(&result), U256::from(10000)); +} + +#[test] +fn basic_get_bytecode() { + let (init_code, verbatim_body) = { + let init = ""; + let body = r#" +# get call payload size +push1 0x20 +calldatasize +sub +# store payload to mem 0x00 +push1 0x20 +push1 0x00 +calldatacopy +return +"#; + + let body_bytecode = { + let mut ret = Vec::new(); + let mut ingest = etk_asm::ingest::Ingest::new(&mut ret); + ingest.ingest("body", body).unwrap(); + ret + }; + + (asm::new_contract("get_bytecode", init, body).unwrap(), body_bytecode) + }; + + let mut rt = util::construct_and_verify(init_code); + + rt.reset(); + rt.expect_validate_caller_any(); + let returned_bytecode_cid: Cid = rt + .call::(evm::Method::GetBytecode as u64, None) + .unwrap() + .unwrap() + .deserialize() + .unwrap(); + rt.verify(); + + let bytecode = rt.store.get(&returned_bytecode_cid).unwrap().unwrap(); + + assert_eq!(bytecode.as_slice(), verbatim_body.as_slice()); +} + +#[test] +fn basic_get_storage_at() { + let init_code = { + // Initialize storage entry on key 0x8965 during init. + let init = r" +push2 0xfffa +push2 0x8965 +sstore"; + let body = r#"return"#; + + asm::new_contract("get_storage_at", init, body).unwrap() + }; + + let mut rt = util::construct_and_verify(init_code); + + rt.reset(); + let params = evm::GetStorageAtParams { storage_key: 0x8965.into() }; + + let sender = Address::new_id(0); // zero address because this method is not invokable on-chain + rt.expect_validate_caller_addr(vec![sender]); + rt.caller = sender; + + // + // Get the storage key that was initialized in the init code. + // + let value: U256 = rt + .call::( + evm::Method::GetStorageAt as u64, + IpldBlock::serialize_cbor(¶ms).unwrap(), + ) + .unwrap() + .unwrap() + .deserialize() + .unwrap(); + rt.verify(); + rt.reset(); + + assert_eq!(U256::from(0xfffa), value); + + // + // Get a storage key that doesn't exist, should default to zero. + // + let params = evm::GetStorageAtParams { storage_key: 0xaaaa.into() }; + + rt.expect_validate_caller_addr(vec![sender]); + let value: U256 = rt + .call::( + evm::Method::GetStorageAt as u64, + IpldBlock::serialize_cbor(¶ms).unwrap(), + ) + .unwrap() + .unwrap() + .deserialize() + .unwrap(); + + assert_eq!(U256::from(0), value); + rt.verify(); +} + +#[test] +fn test_push_last_byte() { + // 60 01 # len + // 80 # dup len + // 60 0b # offset 0x0b + // 60 0 # mem offset 0 + // 39 # codecopy (dstOff, off, len) + // # stack = [0x01] + // 60 0 # mem offset 0 + // f3 # return (offset, size) + // 7f # (bytecode) + + // // Inputs[1] { @000A memory[0x00:0x01] } + // 0000 60 PUSH1 0x01 + // 0002 80 DUP1 + // 0003 60 PUSH1 0x0b + // 0005 60 PUSH1 0x00 + // 0007 39 CODECOPY + // 0008 60 PUSH1 0x00 + // 000A F3 *RETURN + // // Stack delta = +0 + // // Outputs[2] + // // { + // // @0007 memory[0x00:0x01] = code[0x0b:0x0c] + // // @000A return memory[0x00:0x01]; + // // } + // // Block terminates + + // 000B 7F PUSH32 0x + + // function main() { + // memory[0x00:0x01] = code[0x0b:0x0c]; + // return memory[0x00:0x01]; + // } + + // bytecode where push32 opcode is the last/only byte + let init_code = hex::decode("600180600b6000396000f37f").unwrap(); + + let mut rt = util::construct_and_verify(init_code); + + util::invoke_contract(&mut rt, &[]); +} diff --git a/actors/evm/tests/calc.rs b/actors/evm/tests/calc.rs new file mode 100644 index 000000000..133dbbbaf --- /dev/null +++ b/actors/evm/tests/calc.rs @@ -0,0 +1,95 @@ +mod asm; + +use fil_actors_evm_shared::uints::U256; + +mod util; + +#[allow(dead_code)] +pub fn magic_calc_contract() -> Vec { + let init = r#" +push1 0x42 # magic value +push1 0x00 # key of magic value +sstore +"#; + let body = r#" +# method dispatch: +# - 0x00000000 -> magic value +# - 0x00000001 -> ADD arg, magic value +# - 0x00000002 -> MUL arg, magic value + +%dispatch_begin() +%dispatch(0x00, get_magic) +%dispatch(0x01, add_magic) +%dispatch(0x02, mul_magic) +%dispatch_end() + +#### method implementation +get_magic: +jumpdest +push1 0x20 # length of return data +push1 0x00 # key of magic +sload +push1 0x00 # return memory offset +mstore +push1 0x00 +return + +add_magic: +jumpdest +push1 0x20 # length of return data +push1 0x04 +calldataload # arg1 +push1 0x00 # key of magic +sload +add +push1 0x00 # return memory offset +mstore +push1 0x00 +return + +mul_magic: +jumpdest +push1 0x20 # length of return data +push1 0x04 +calldataload # arg1 +push1 0x00 # key of magic +sload +mul +push1 0x00 # return memory offset +mstore +push1 0x00 +return + +"#; + + asm::new_contract("magic-calc", init, body).unwrap() +} + +#[test] +fn test_magic_calc() { + let contract = magic_calc_contract(); + + let mut rt = util::construct_and_verify(contract); + + // invoke contract -- get_magic + let contract_params = vec![0u8; 32]; + + let result = util::invoke_contract(&mut rt, &contract_params); + assert_eq!(U256::from_big_endian(&result), U256::from(0x42)); + + // invoke contract -- add_magic + let mut contract_params = vec![0u8; 36]; + contract_params[3] = 0x01; + contract_params[35] = 0x01; + + let result = util::invoke_contract(&mut rt, &contract_params); + assert_eq!(U256::from_big_endian(&result), U256::from(0x43)); + + // invoke contract -- mul_magic + let mut contract_params = vec![0u8; 36]; + contract_params[3] = 0x02; + contract_params[35] = 0x02; + + let result = util::invoke_contract(&mut rt, &contract_params); + assert_eq!(U256::from_big_endian(&result), U256::from(0x84)); +} diff --git a/actors/evm/tests/call.rs b/actors/evm/tests/call.rs new file mode 100644 index 000000000..5faf31360 --- /dev/null +++ b/actors/evm/tests/call.rs @@ -0,0 +1,1213 @@ +mod asm; + +use std::fmt::Debug; +use std::sync::Arc; + +use ethers::abi::Detokenize; +use ethers::prelude::builders::ContractCall; +use ethers::prelude::*; +use ethers::providers::{MockProvider, Provider}; +use ethers::types::Bytes; +use evm::{Method, EVM_CONTRACT_REVERTED}; +use fil_actor_evm as evm; +use fil_actors_evm_shared::address::EthAddress; +use fil_actors_evm_shared::uints::U256; +use fil_actors_runtime::{test_utils::*, EAM_ACTOR_ID, INIT_ACTOR_ADDR}; +use fvm_ipld_encoding::ipld_block::IpldBlock; +use fvm_ipld_encoding::{BytesDe, BytesSer, CBOR, IPLD_RAW}; +use fvm_shared::address::Address as FILAddress; +use fvm_shared::address::Address; +use fvm_shared::bigint::Zero; +use fvm_shared::econ::TokenAmount; +use fvm_shared::error::{ErrorNumber, ExitCode}; +use fvm_shared::sys::SendFlags; +use fvm_shared::{ActorID, MethodNum}; +use once_cell::sync::Lazy; + +mod util; + +#[allow(dead_code)] +pub fn call_proxy_contract() -> Vec { + let init = ""; + let body = r#" +# this contract takes an address and the call payload and proxies a call to that address +# get call payload size +push1 0x20 +calldatasize +sub +# store payload to mem 0x00 +push1 0x20 +push1 0x00 +calldatacopy + +# prepare the proxy call +# output offset and size -- 0 in this case, we use returndata +push2 0x00 +push1 0x00 +# input offset and size +push1 0x20 +calldatasize +sub +push1 0x00 +# value +push1 0x00 +# dest address +push1 0x00 +calldataload +# gas +push4 0xffffffff +# do the call +call + +# return result through +returndatasize +push1 0x00 +push1 0x00 +returndatacopy +returndatasize +push1 0x00 +return +"#; + + asm::new_contract("call-proxy", init, body).unwrap() +} + +#[allow(dead_code)] +pub fn call_proxy_transfer_contract() -> Vec { + let init = ""; + let body = r#" +# this contract takes an address and the call payload and proxies a call to that address +# get call payload size +push1 0x20 +calldatasize +sub +# store payload to mem 0x00 +push1 0x20 +push1 0x00 +calldatacopy + +# prepare the proxy call +# output offset and size -- 0 in this case, we use returndata +push2 0x00 +push1 0x00 +# input offset and size +push1 0x20 +calldatasize +sub +push1 0x00 +# value +push1 0x42 +# dest address +push1 0x00 +calldataload +# gas +push1 0x00 +# do the call +call + +# return result through +returndatasize +push1 0x00 +push1 0x00 +returndatacopy +returndatasize +push1 0x00 +return +"#; + + asm::new_contract("call-proxy-transfer", init, body).unwrap() +} + +#[allow(dead_code)] +pub fn call_proxy_gas2300_contract() -> Vec { + let init = ""; + let body = r#" +# this contract takes an address and the call payload and proxies a call to that address +# get call payload size +push1 0x20 +calldatasize +sub +# store payload to mem 0x00 +push1 0x20 +push1 0x00 +calldatacopy + +# prepare the proxy call +# output offset and size -- 0 in this case, we use returndata +push2 0x00 +push1 0x00 +# input offset and size +push1 0x20 +calldatasize +sub +push1 0x00 +# value +push1 0x00 +# dest address +push1 0x00 +calldataload +# gas +%push(2300) +# do the call +call + +# return result through +returndatasize +push1 0x00 +push1 0x00 +returndatacopy +returndatasize +push1 0x00 +return +"#; + + asm::new_contract("call-proxy-gas2300", init, body).unwrap() +} + +#[test] +fn test_call() { + let contract = call_proxy_contract(); + + // construct the proxy + let mut rt = util::construct_and_verify(contract); + + // create a mock target and proxy a call through the proxy + let target_id = 0x100; + let target = FILAddress::new_id(target_id); + let evm_target = EthAddress(hex_literal::hex!("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef")); + let f4_target: FILAddress = evm_target.try_into().unwrap(); + rt.actor_code_cids.insert(target, *EVM_ACTOR_CODE_ID); + rt.set_delegated_address(target.id().unwrap(), f4_target); + + let evm_target_word = evm_target.as_evm_word(); + + // dest + method 0 with no data + let mut contract_params = vec![0u8; 36]; + evm_target_word.to_big_endian(&mut contract_params[..32]); + + let proxy_call_contract_params = vec![0u8; 4]; + let proxy_call_input_data = make_raw_params(proxy_call_contract_params); + + // expected return data + let mut return_data = vec![0u8; 32]; + return_data[31] = 0x42; + + rt.expect_gas_available(10_000_000_000u64); + rt.expect_send( + f4_target, + evm::Method::InvokeContract as u64, + proxy_call_input_data, + TokenAmount::zero(), + Some(0xffffffff), + SendFlags::empty(), + IpldBlock::serialize_cbor(&BytesSer(&return_data)) + .expect("failed to serialize return data"), + ExitCode::OK, + None, + ); + + let result = util::invoke_contract(&mut rt, &contract_params); + assert_eq!(U256::from_big_endian(&result), U256::from(0x42)); +} + +const TRANSFER_GAS_VALUE: u64 = 10_000_000; + +// Make sure we set the correct gas limit with value and 0 gas. +#[test] +fn test_transfer_nogas() { + let contract = call_proxy_transfer_contract(); + + // construct the proxy + let mut rt = util::construct_and_verify(contract); + + // create a mock actor and proxy a call through the proxy + let target_id = 0x100; + let target = FILAddress::new_id(target_id); + rt.actor_code_cids.insert(target, *EVM_ACTOR_CODE_ID); + + let evm_target_word = EthAddress::from_id(target_id).as_evm_word(); + + // dest with no data + let mut contract_params = vec![0u8; 32]; + evm_target_word.to_big_endian(&mut contract_params); + + // we don't expected return data + let return_data = vec![]; + + rt.expect_gas_available(TRANSFER_GAS_VALUE * 10); + rt.expect_send( + target, + Method::InvokeContract as u64, + None, + TokenAmount::from_atto(0x42), + Some(TRANSFER_GAS_VALUE), + SendFlags::empty(), + IpldBlock::serialize_cbor(&BytesSer(&return_data)) + .expect("failed to serialize return data"), + ExitCode::OK, + None, + ); + + let result = util::invoke_contract(&mut rt, &contract_params); + assert_eq!(U256::from_big_endian(&result), U256::from(0)); + rt.verify(); +} + +// Make sure we set the correct gas limit with no value and 2300 gas. +#[test] +fn test_transfer_2300() { + let contract = call_proxy_gas2300_contract(); + + // construct the proxy + let mut rt = util::construct_and_verify(contract); + + // create a mock actor and proxy a call through the proxy + let target_id = 0x100; + let target = FILAddress::new_id(target_id); + rt.actor_code_cids.insert(target, *EVM_ACTOR_CODE_ID); + + let evm_target_word = EthAddress::from_id(target_id).as_evm_word(); + + // dest with no data + let mut contract_params = vec![0u8; 32]; + evm_target_word.to_big_endian(&mut contract_params); + + // we don't expected return data + let return_data = vec![]; + + rt.expect_gas_available(TRANSFER_GAS_VALUE * 10); + rt.expect_send( + target, + Method::InvokeContract as u64, + None, + TokenAmount::zero(), + Some(TRANSFER_GAS_VALUE), + SendFlags::empty(), + IpldBlock::serialize_cbor(&BytesSer(&return_data)) + .expect("failed to serialize return data"), + ExitCode::OK, + None, + ); + + let result = util::invoke_contract(&mut rt, &contract_params); + assert_eq!(U256::from_big_endian(&result), U256::from(0)); + rt.verify(); +} + +#[test] +pub fn test_call_output_region() { + let init = ""; + let body = r#" +# this contract truncates return from send to output length + +# prepare the proxy call +push1 0x00 +calldataload # size from first word +push1 0x00 # offset + +# input offset and size +push1 0x00 +push1 0x00 + +# value +push1 0x00 + +# dest address +push1 0x20 +calldataload # address from second word + +# gas +push1 0x00 + +# do the call +call + +push1 0x40 +calldataload # return size from third word +push1 0x00 # offset +return +"#; + + let contract = asm::new_contract("call-output-region", init, body).unwrap(); + let mut rt = util::construct_and_verify(contract); + + let address = EthAddress(util::CONTRACT_ADDRESS); + + // large set of data + let large_ret = IpldBlock { codec: CBOR, data: vec![0xff; 2048] }; + + let cases = [(32, 64), (64, 64), (1024, 1025)]; + for (output_size, return_size) in cases { + rt.expect_send( + (&address).try_into().unwrap(), + Method::InvokeContract as u64, + None, + TokenAmount::zero(), + Some(0), + SendFlags::empty(), + Some(large_ret.clone()), + ExitCode::OK, + None, + ); + + rt.expect_gas_available(10_000_000_000); + + let out = util::invoke_contract( + &mut rt, + &[ + U256::from(output_size).to_bytes().to_vec(), + address.as_evm_word().to_bytes().to_vec(), + U256::from(return_size).to_bytes().to_vec(), + ] + .concat(), + ); + let mut expected = vec![0xff; output_size]; + expected.extend_from_slice(&vec![0u8; return_size - output_size]); + + rt.verify(); + assert_eq!( + expected, + out, + "expect: {}\n got: {}", + hex::encode(&expected), + hex::encode(&out) + ); + rt.reset(); + } +} + +#[allow(dead_code)] +pub fn filecoin_fallback_contract() -> Vec { + hex::decode(include_str!("contracts/FilecoinFallback.hex")).unwrap() +} + +#[allow(dead_code)] +pub fn filecoin_call_actor_contract() -> Vec { + hex::decode(include_str!("contracts/CallActorPrecompile.hex")).unwrap() +} + +#[test] +fn test_reserved_method() { + let contract = filecoin_fallback_contract(); + let mut rt = util::construct_and_verify(contract); + + let code = rt.call::(0x42, None).unwrap_err().exit_code(); + assert_eq!(ExitCode::USR_UNHANDLED_MESSAGE, code); +} + +#[test] +fn test_native_call() { + let contract = filecoin_fallback_contract(); + let mut rt = util::construct_and_verify(contract); + + rt.expect_validate_caller_any(); + let result = rt.call::(1024, None).unwrap(); + assert_eq!(result, None); + + rt.expect_validate_caller_any(); + let result = rt.call::(1025, None).unwrap(); + assert_eq!(result, Some(IpldBlock { codec: CBOR, data: "foobar".into() })); + + rt.expect_validate_caller_any(); + let mut err = rt.call::(1026, None).unwrap_err(); + assert_eq!(err.exit_code().value(), 42); + assert!(err.take_data().is_none()); + + rt.expect_validate_caller_any(); + let mut err = rt.call::(1027, None).unwrap_err(); + assert_eq!(err.exit_code().value(), 42); + assert_eq!(err.take_data().unwrap().data, &b"foobar"[..]); +} + +#[test] +fn test_callactor_success() { + // Should work if the called actor succeeds. + test_callactor_inner(2048, ExitCode::OK, true) +} + +#[test] +fn test_callactor_revert() { + // Should propagate the return value if the called actor fails. + test_callactor_inner(2048, EVM_CONTRACT_REVERTED, true) +} + +// Much taken from tests/env.rs +abigen!(CallActorPrecompile, "./tests/contracts/CallActorPrecompile.abi"); + +const OWNER_ID: ActorID = 1001; +const _OWNER: Address = Address::new_id(OWNER_ID); +static CONTRACT: Lazy>> = Lazy::new(|| { + // The owner of the contract is expected to be the 160 bit hash used on Ethereum. + // We're not going to use it during the tests. + let address = EthAddress::from_id(OWNER_ID); + let address = ethers::core::types::Address::from_slice(address.as_ref()); + // A dummy client that we don't intend to use to call the contract or send transactions. + let (client, _mock) = Provider::mocked(); + CallActorPrecompile::new(address, Arc::new(client)) +}); + +pub type TestContractCall = ContractCall, R>; + +#[test] +fn test_callactor_restrict() { + // Should propagate the return value if the called actor fails. + test_callactor_inner(2, EVM_CONTRACT_REVERTED, false) +} + +fn test_callactor_inner(method_num: MethodNum, exit_code: ExitCode, valid_call_input: bool) { + let contract = { + let (init, body) = util::PrecompileTest::test_runner_assembly(); + asm::new_contract("call_actor-precompile-test", &init, &body).unwrap() + }; + + const CALLACTOR_NUM_PARAMS: usize = 8; + + // construct the proxy + let mut rt = util::construct_and_verify(contract); + + // create a mock target and proxy a call through the proxy + let target_id = 0x100; + let target = FILAddress::new_id(target_id); + rt.actor_code_cids.insert(target, *EVM_ACTOR_CODE_ID); + + // dest + method with no data + let mut contract_params = Vec::new(); + + let method = U256::from(method_num); + let value = U256::from(0); + let send_flags = SendFlags::default(); + let codec = U256::from(0); + + let proxy_call_input_data = vec![]; + let data_size = U256::from(proxy_call_input_data.len()); + + let target_bytes = target.to_bytes(); + let target_size = U256::from(target_bytes.len()); + + let data_off = U256::from(6 * 32); + let target_off = data_off + 32 + data_size; + + // a bit messy but a "test" for CallActorParams + let params: Vec = CallActorParams { + method: U256::from(method_num), + value: U256::from(0), + flags: U256::from(0), + codec: U256::from(0), + param_offset: data_off, + addr_offset: target_off, + param_len: data_size, + params: Some(proxy_call_input_data.clone()), + addr_len: target_size, + addr: target_bytes.clone(), + } + .into(); + + contract_params.extend_from_slice(&method.to_bytes()); + contract_params.extend_from_slice(&value.to_bytes()); + contract_params.extend_from_slice(&U256::from(send_flags.bits()).to_bytes()); + contract_params.extend_from_slice(&codec.to_bytes()); + contract_params.extend_from_slice(&data_off.to_bytes()); + contract_params.extend_from_slice(&target_off.to_bytes()); + contract_params.extend_from_slice(&data_size.to_bytes()); + contract_params.extend_from_slice(&proxy_call_input_data); + contract_params.extend_from_slice(&target_size.to_bytes()); + contract_params.extend_from_slice(&target_bytes); + + assert_eq!( + params, + contract_params, + "{}\n{}", + hex::encode(¶ms), + hex::encode(&contract_params) + ); + + assert_eq!( + 32 * CALLACTOR_NUM_PARAMS + target_bytes.len() + proxy_call_input_data.len(), + contract_params.len(), + "unexpected input length" + ); + + // expected return data + // Test with a codec _other_ than CBOR/DAG_CBOR, to make sure we are actually passing the returned codec + let some_codec = 0x42; + let data = vec![0xde, 0xad, 0xbe, 0xef]; + let send_return = IpldBlock { codec: some_codec, data }; + + if valid_call_input { + // We only get to the send_generalized if the call params were valid + rt.expect_send( + target, + method_num, + make_raw_params(proxy_call_input_data), + TokenAmount::zero(), + Some(0), + send_flags, + Some(send_return.clone()), + exit_code, + None, + ); + } + + // output bytes are padded to nearest 32 byte + let mut v = vec![0; 32]; + v[..4].copy_from_slice(&send_return.data); + + let expect = CallActorReturn { + send_exit_code: U256::from(exit_code.value()), + codec: send_return.codec, + data_offset: 96, + data_size: send_return.data.len() as u32, + data: v, + }; + + let (expected_exit, expected_out) = if valid_call_input { + (util::PrecompileExit::Success, expect.into()) + } else { + (util::PrecompileExit::Reverted, vec![]) + }; + + let test = util::PrecompileTest { + expected_exit_code: expected_exit, + precompile_address: util::NativePrecompile::CallActor.eth_address(), + output_size: 32, + gas_avaliable: 10_000_000_000u64, + expected_return: expected_out, + call_op: util::PrecompileCallOpcode::DelegateCall, + input: contract_params, + }; + + // invoke + test.run_test(&mut rt); +} + +#[test] +fn call_actor_weird_offset() { + let contract = { + let (init, body) = util::PrecompileTest::test_runner_assembly(); + asm::new_contract("call_actor-precompile-test", &init, &body).unwrap() + }; + let mut rt = util::construct_and_verify(contract); + + let addr = Address::new_delegated(1234, b"foobarboxy").unwrap(); + let addr_bytes = addr.to_bytes(); + let params = CallActorParams { + method: U256::from(0), + value: U256::from(0), + flags: U256::from(0), + codec: U256::from(0), + param_offset: U256::from(200), + addr_offset: U256::from(300), + param_len: U256::from(0), + params: None, + addr_len: U256::from(addr_bytes.len()), + addr: addr_bytes, + }; + + let input: Vec = params.into(); + + let mut test = util::PrecompileTest { + expected_exit_code: util::PrecompileExit::Success, + precompile_address: util::NativePrecompile::CallActor.eth_address(), + output_size: 32, + gas_avaliable: 10_000_000_000u64, + expected_return: vec![], + call_op: util::PrecompileCallOpcode::DelegateCall, + input, + }; + + rt.expect_send( + addr, + 0, + None, + TokenAmount::zero(), + Some(0), + SendFlags::empty(), + None, + ExitCode::OK, + None, + ); + + let precompile_return = CallActorReturn::default(); + + test.run_test_expecting(&mut rt, precompile_return, util::PrecompileExit::Success); +} + +#[test] +fn call_actor_overlapping() { + let contract = { + let (init, body) = util::PrecompileTest::test_runner_assembly(); + asm::new_contract("call_actor-precompile-test", &init, &body).unwrap() + }; + let mut rt = util::construct_and_verify(contract); + let addr = Address::new_delegated(1234, b"foobarboxy").unwrap(); + + let mut call_params = CallActorParams::default(); + + // not valid CBOR, but params should parse fine in precompile + let addr_bytes = addr.to_bytes(); + call_params.codec(U256::from(CBOR)); + + call_params.param_offset = U256::from(CallActorParams::FIRST_DYNAMIC_OFFSET); + call_params.param_len = U256::from(addr_bytes.len()); + call_params.params = None; + + call_params.addr_offset = U256::from(CallActorParams::FIRST_DYNAMIC_OFFSET); + call_params.addr_len = U256::from(addr_bytes.len()); + call_params.addr = addr_bytes.clone(); + + let mut test = util::PrecompileTest { + precompile_address: util::NativePrecompile::CallActor.eth_address(), + output_size: 32, + gas_avaliable: 10_000_000_000u64, + call_op: util::PrecompileCallOpcode::DelegateCall, + // overwritten in tests + expected_return: vec![], + expected_exit_code: util::PrecompileExit::Success, + input: call_params.clone().into(), + }; + + rt.expect_send( + addr, + 0, + Some(IpldBlock { codec: CBOR, data: addr_bytes }), + TokenAmount::zero(), + Some(0), + SendFlags::empty(), + None, + ExitCode::OK, + None, + ); + + test.input = call_params.into(); + test.run_test_expecting(&mut rt, CallActorReturn::default(), util::PrecompileExit::Success); +} + +#[test] +fn call_actor_id_with_full_address() { + let contract = { + let (init, body) = util::PrecompileTest::test_runner_assembly(); + asm::new_contract("call_actor-precompile-test", &init, &body).unwrap() + }; + let mut rt = util::construct_and_verify(contract); + let addr = Address::new_delegated(1234, b"foobarboxy").unwrap(); + let actual_id_addr = 1234; + + let mut call_params = CallActorParams::default(); + // garbage bytes + call_params.set_addr(CallActorParams::EMPTY_PARAM_ADDR_OFFSET, addr.to_bytes()); + // id address + call_params.addr_offset = U256::from(actual_id_addr); + + let mut test = util::PrecompileTest { + precompile_address: util::NativePrecompile::CallActorId.eth_address(), + output_size: 32, + gas_avaliable: 10_000_000_000u64, + call_op: util::PrecompileCallOpcode::DelegateCall, + // overwritten in tests + expected_return: vec![], + expected_exit_code: util::PrecompileExit::Success, + input: call_params.clone().into(), + }; + + rt.expect_send( + Address::new_id(actual_id_addr), + 0, + None, + TokenAmount::zero(), + Some(0), + SendFlags::empty(), + None, + ExitCode::OK, + None, + ); + + test.input = call_params.into(); + test.run_test_expecting(&mut rt, CallActorReturn::default(), util::PrecompileExit::Success); +} + +#[test] +fn call_actor_syscall_error() { + let contract = { + let (init, body) = util::PrecompileTest::test_runner_assembly(); + asm::new_contract("call_actor-precompile-test", &init, &body).unwrap() + }; + let mut rt = util::construct_and_verify(contract); + let addr = Address::new_delegated(1234, b"foobarboxy").unwrap(); + + let mut call_params = CallActorParams::default(); + call_params.set_addr(CallActorParams::EMPTY_PARAM_ADDR_OFFSET, addr.to_bytes()); + + let mut test = util::PrecompileTest { + precompile_address: util::NativePrecompile::CallActor.eth_address(), + output_size: 32, + gas_avaliable: 10_000_000_000u64, + call_op: util::PrecompileCallOpcode::DelegateCall, + // overwritten in tests + expected_return: vec![], + expected_exit_code: util::PrecompileExit::Success, + input: call_params.clone().into(), + }; + + let syscall_exit = ErrorNumber::NotFound; + + let expect = CallActorReturn { + send_exit_code: U256::from(syscall_exit as u32).i256_neg(), + ..Default::default() + }; + + rt.expect_send( + addr, + 0, + None, + TokenAmount::zero(), + Some(0), + SendFlags::empty(), + None, + ExitCode::new(0xffff), + Some(syscall_exit), + ); + + test.input = call_params.into(); + test.run_test_expecting(&mut rt, expect, util::PrecompileExit::Success); +} + +#[cfg(test)] +mod call_actor_invalid { + use super::*; + + fn bad_params_inner(mut call_params: CallActorParams, addr: Address, set_addr: bool) { + let contract = { + let (init, body) = util::PrecompileTest::test_runner_assembly(); + asm::new_contract("call_actor-precompile-test", &init, &body).unwrap() + }; + let mut rt = util::construct_and_verify(contract); + + let mut test = util::PrecompileTest { + precompile_address: util::NativePrecompile::CallActor.eth_address(), + output_size: 32, + gas_avaliable: 10_000_000_000u64, + call_op: util::PrecompileCallOpcode::DelegateCall, + // overwritten in tests + expected_return: vec![], + expected_exit_code: util::PrecompileExit::Success, + input: call_params.clone().into(), + }; + + if set_addr { + call_params.set_addr(CallActorParams::EMPTY_PARAM_ADDR_OFFSET, addr.to_bytes()); + } + + test.input = call_params.into(); + test.run_test_expecting(&mut rt, vec![], util::PrecompileExit::Reverted); + } + + #[test] + fn no_address() { + let addr = Address::new_delegated(1234, b"foobarboxy").unwrap(); + + let mut call_params = CallActorParams::default(); + call_params.set_addr(CallActorParams::EMPTY_PARAM_ADDR_OFFSET, vec![]); + bad_params_inner(call_params, addr, false) + } + + #[test] + fn invalid_codec() { + let addr = Address::new_delegated(1234, b"foobarboxy").unwrap(); + + let mut call_params = CallActorParams::default(); + call_params.codec(U256([0xff, 0, 0, 0])); + bad_params_inner(call_params, addr, true) + } + + #[test] + fn invalid_method() { + let addr = Address::new_delegated(1234, b"foobarboxy").unwrap(); + + let mut call_params = CallActorParams::default(); + call_params.method(U256([0xff, 0, 0, 0])); + bad_params_inner(call_params, addr, true) + } + + #[test] + fn invalid_params_zero_codec() { + let addr = Address::new_delegated(1234, b"foobarboxy").unwrap(); + + let mut call_params = CallActorParams::default(); + let send_params = vec![0xff; 32]; + call_params + .set_params(CallActorParams::FIRST_DYNAMIC_OFFSET, Some(send_params)) + .set_addr(CallActorParams::EMPTY_PARAM_ADDR_OFFSET + 32, addr.to_bytes()); + + bad_params_inner(call_params, addr, false) + } + + #[test] + fn invalid_params_zero_codec_2() { + let addr = Address::new_delegated(1234, b"foobarboxy").unwrap(); + + let mut call_params = CallActorParams::default(); + let send_params = vec![0]; + call_params + .set_params(CallActorParams::FIRST_DYNAMIC_OFFSET, Some(send_params)) + .set_addr(CallActorParams::EMPTY_PARAM_ADDR_OFFSET + 1, addr.to_bytes()); + + bad_params_inner(call_params, addr, false) + } +} + +#[derive(Debug, Clone)] +struct CallActorParams { + method: U256, + value: U256, + flags: U256, + codec: U256, + param_offset: U256, + addr_offset: U256, + param_len: U256, + params: Option>, + addr_len: U256, + addr: Vec, +} + +impl Default for CallActorParams { + fn default() -> Self { + Self { + method: U256::from(0), + value: U256::from(0), + flags: U256::from(0), + codec: U256::from(0), + // right after static params + param_offset: U256::from(Self::FIRST_DYNAMIC_OFFSET), + addr_offset: U256::from(Self::EMPTY_PARAM_ADDR_OFFSET), + // no len dynamic values + param_len: U256::from(0), + params: None, + addr_len: U256::from(0), + addr: vec![], + } + } +} + +impl CallActorParams { + /// method, value, flags, codec, param_off, addr_off + /// usually the param offset + const FIRST_DYNAMIC_OFFSET: usize = 6 * 32; + + const EMPTY_PARAM_ADDR_OFFSET: usize = Self::FIRST_DYNAMIC_OFFSET + 32; + + pub fn set_addr(&mut self, offset: usize, addr: Vec) -> &mut Self { + self.addr_len = U256::from(addr.len()); + self.addr_offset = U256::from(offset); + self.addr = addr; + self + } + + pub fn codec(&mut self, codec: U256) -> &mut Self { + self.codec = codec; + self + } + + #[allow(unused)] + pub fn value(&mut self, value: U256) -> &mut Self { + self.value = value; + self + } + + pub fn method(&mut self, codec: U256) -> &mut Self { + self.codec = codec; + self + } + + pub fn set_params(&mut self, offset: usize, params: Option>) -> &mut Self { + self.param_len = U256::from(params.clone().unwrap_or_default().len()); + self.param_offset = U256::from(offset); + self.params = params; + self + } +} + +impl From for Vec { + // mriise: apologies for whoever needs to change this in the future. + fn from(src: CallActorParams) -> Self { + let param_offset = src.param_offset.as_usize(); + let addr_offset = src.addr_offset.as_usize(); + + let param_len_usize = src.param_len.as_usize(); + let addr_len_usize = src.addr_len.as_usize(); + + let mut out = + [src.method, src.value, src.flags, src.codec, src.param_offset, src.addr_offset] + .iter() + .map(|p| p.to_bytes().to_vec()) + .collect::>>() + .concat(); + + assert_eq!(out.len(), CallActorParams::FIRST_DYNAMIC_OFFSET); + assert!(param_offset >= out.len()); + + let addr_len_offset = addr_offset; + let addr_begin = addr_len_offset + 32; + let addr_end = addr_begin + addr_len_usize; + + let param_len_offset = param_offset; + let param_begin = param_len_offset + 32; + let param_end = param_begin + param_len_usize; + + out.resize_with(addr_end, || 0); + + let param_len = src.param_len.to_bytes(); + let addr_len = src.addr_len.to_bytes(); + + // write single word len values first + out[param_len_offset..param_len_offset + 32].copy_from_slice(¶m_len); + out[addr_len_offset..addr_len_offset + 32].copy_from_slice(&addr_len); + + // then write actual data immediately after len + if let Some(params) = src.params.clone() { + assert!(addr_offset >= param_offset + param_len_usize); + out[param_begin..param_end].copy_from_slice(¶ms) + } + out[addr_begin..addr_end].copy_from_slice(&src.addr); + + // log::debug!("params\n[{}:32] {}\n[{}:{}] {}", param_len_offset, hex::encode(param_len), param_begin, param_end, hex::encode(&src.params.unwrap_or_default())); + // log::debug!("address\n[{}:32] {}\n[{}:{}] {}", addr_len_offset, hex::encode(addr_len), addr_begin, addr_end, hex::encode(&src.addr)); + + out + } +} + +impl Default for CallActorReturn { + fn default() -> Self { + Self { + send_exit_code: U256::from(ExitCode::OK.value()), + codec: 0, + data_offset: 3 * 32, + data_size: 0, + data: vec![], + } + } +} + +#[derive(Debug, PartialEq, Eq)] +struct CallActorReturn { + send_exit_code: U256, + codec: u64, + data_offset: u32, + data_size: u32, + data: Vec, +} + +impl From for Vec { + fn from(src: CallActorReturn) -> Self { + // precompile will return negative number for send errors + let exit_code = src.send_exit_code; + + let codec = U256::from(src.codec); + let offset = U256::from(src.data_offset); + let len = U256::from(src.data_size); + + let mut out = [exit_code, codec, offset, len] + .iter() + .map(|p| p.to_bytes().to_vec()) + .collect::>>() + .concat(); + + if src.data.len() != src.data_size as usize { + log::warn!( + "actor return data length {} does not match specified size {}", + src.data.len(), + src.data_size + ) + } + out.extend_from_slice(&src.data); + out + } +} + +fn make_raw_params(bytes: Vec) -> Option { + if bytes.is_empty() { + return None; + } + Some(IpldBlock { codec: IPLD_RAW, data: bytes }) +} + +#[test] +fn call_actor_solidity() { + // solidity + let contract_hex = include_str!("contracts/CallActorPrecompile.hex"); + // let mut contract_rt = new_call_actor_contract(); + let contract_address = EthAddress(util::CONTRACT_ADDRESS); + let mut tester = ContractTester::new(contract_address, 111, contract_hex); + + // call_actor_id + { + let params = + CONTRACT.call_actor_id(0, ethers::types::U256::zero(), 0, 0, Bytes::default(), 101); + + let expected_return = vec![0xff, 0xfe]; + tester.rt.expect_send( + Address::new_id(101), + 0, + None, + TokenAmount::from_atto(0), + Some(9843750), + SendFlags::empty(), + Some(IpldBlock { codec: 0, data: expected_return.clone() }), + ExitCode::OK, + None, + ); + + let (success, exit, codec, ret_val): (bool, ethers::types::I256, u64, Bytes) = + tester.call(params); + + assert!(success); + assert_eq!(exit, I256::from(0)); + assert_eq!(codec, 0); + assert_eq!(&ret_val, &expected_return, "got {}", hex::encode(&ret_val)); + } + tester.rt.reset(); + // call_actor + { + log::warn!("new test"); + // EVM actor + let evm_target = 10101; + let evm_del = EthAddress(util::CONTRACT_ADDRESS).try_into().unwrap(); + tester.rt.set_delegated_address(evm_target, evm_del); + + let to_address = { + let subaddr = hex_literal::hex!("b0ba000000000000000000000000000000000000"); + Address::new_delegated(EAM_ACTOR_ID, &subaddr).unwrap() + }; + let params = CONTRACT.call_actor_address( + 0, + ethers::types::U256::zero(), + 0, + 0, + Bytes::default(), + to_address.to_bytes().into(), + ); + + let expected_return = vec![0xff, 0xfe]; + tester.rt.expect_send( + to_address, + 0, + None, + TokenAmount::from_atto(0), + Some(9843750), + SendFlags::empty(), + Some(IpldBlock { codec: 0, data: expected_return.clone() }), + ExitCode::OK, + None, + ); + + let (success, exit, codec, ret_val): (bool, ethers::types::I256, u64, Bytes) = + tester.call(params); + + assert!(success); + assert_eq!(exit, I256::from(0)); + assert_eq!(codec, 0); + assert_eq!(&ret_val, &expected_return, "got {}", hex::encode(&ret_val)); + } +} + +#[test] +fn call_actor_send_solidity() { + // solidity + let contract_hex = include_str!("contracts/CallActorPrecompile.hex"); + // let mut contract_rt = new_call_actor_contract(); + let contract_address = EthAddress(util::CONTRACT_ADDRESS); + let mut tester = ContractTester::new(contract_address, 111, contract_hex); + + // send 1 atto Fil (this should be a full integration tests rly) + { + let params = + CONTRACT.call_actor_id(0, ethers::types::U256::from(1), 0, 0, Bytes::default(), 101); + + tester.rt.add_id_address( + Address::new_delegated(12345, b"foobarboxy").unwrap(), + Address::new_id(101), + ); + + tester.rt.add_balance(TokenAmount::from_atto(100)); + + let expected_return = vec![0xff, 0xfe]; + tester.rt.expect_send( + Address::new_id(101), + 0, + None, + TokenAmount::from_atto(1), + Some(9843750), + SendFlags::empty(), + Some(IpldBlock { codec: 0, data: expected_return.clone() }), + ExitCode::OK, + None, + ); + + let (success, exit, codec, ret_val): (bool, ethers::types::I256, u64, Bytes) = + tester.call(params); + + assert!(success); + assert_eq!(exit, I256::from(0)); + assert_eq!(codec, 0); + assert_eq!(&ret_val, &expected_return, "got {}", hex::encode(&ret_val)); + assert_eq!(tester.rt.get_balance(), TokenAmount::from_atto(99)); + } +} + +pub(crate) struct ContractTester { + rt: MockRuntime, + _address: EthAddress, +} + +impl ContractTester { + fn new(addr: EthAddress, id: u64, contract_hex: &str) -> Self { + init_logging().ok(); + + let mut rt = MockRuntime::default(); + let params = evm::ConstructorParams { + creator: EthAddress::from_id(EAM_ACTOR_ID), + initcode: hex::decode(contract_hex).unwrap().into(), + }; + rt.add_id_address(addr.try_into().unwrap(), FILAddress::new_id(id)); + + // invoke constructor + rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); + rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); + + rt.set_origin(FILAddress::new_id(0)); + // first actor created is 0 + rt.set_delegated_address(0, Address::new_delegated(EAM_ACTOR_ID, &addr.0).unwrap()); + + assert!(rt + .call::( + evm::Method::Constructor as u64, + IpldBlock::serialize_cbor(¶ms).unwrap(), + ) + .unwrap() + .is_none()); + + rt.verify(); + rt.reset(); + Self { rt, _address: addr } + } + + fn call(&mut self, call: TestContractCall) -> Returns { + let input = call.calldata().expect("Should have calldata.").to_vec(); + let input = + IpldBlock::serialize_cbor(&BytesSer(&input)).expect("failed to serialize input data"); + + self.rt.expect_validate_caller_any(); + self.rt.expect_gas_available(10_000_000); + self.rt.expect_gas_available(10_000_000); + + let BytesDe(result) = self + .rt + .call::(evm::Method::InvokeContract as u64, input) + .unwrap() + .unwrap() + .deserialize() + .unwrap(); + + decode_function_data(&call.function, result, false).unwrap() + } +} diff --git a/actors/evm/tests/context_opcodes.rs b/actors/evm/tests/context_opcodes.rs new file mode 100644 index 000000000..9f4119eb0 --- /dev/null +++ b/actors/evm/tests/context_opcodes.rs @@ -0,0 +1,97 @@ +// randomness from two different contracts + +mod asm; +mod util; + +use crate::util::dispatch_num_word; +use rand::prelude::*; + +#[allow(dead_code)] +pub fn prevrandao_contract() -> Vec { + let init = ""; + let body = r#" +%dispatch_begin() +%dispatch(0x00, basic) +%dispatch(0x01, cache) +%dispatch_end() + +basic: + jumpdest + difficulty + %return_stack_word() + +cache: + jumpdest + # push store first randomness at 0x00 + difficulty + push1 0x00 + mstore + # store second randomness at 0x20 + difficulty + push1 0x20 + mstore + + # return both values 0x00-0x40 + push1 0x40 + push1 0x00 + return +"#; + + asm::new_contract("prevrandao", init, body).unwrap() +} + +#[test] +fn test_prevrandao() { + let mut rt = util::construct_and_verify(prevrandao_contract()); + rt.epoch = 101; + + // simple test + { + rt.expect_get_randomness_from_beacon( + fil_actors_runtime::runtime::DomainSeparationTag::EvmPrevRandao, + 101, + b"prevrandao".to_vec(), + [0u8; 32], + ); + + let result = util::invoke_contract(&mut rt, &dispatch_num_word(0)); + rt.verify(); + assert_eq!(result, [0u8; 32], "expected empty randomness"); + rt.reset(); + } + + let mut rand = thread_rng(); + + // actual random value + { + let expected: [u8; 32] = rand.gen(); + rt.expect_get_randomness_from_beacon( + fil_actors_runtime::runtime::DomainSeparationTag::EvmPrevRandao, + 101, + b"prevrandao".to_vec(), + expected, + ); + + let result = util::invoke_contract(&mut rt, &dispatch_num_word(0)); + rt.verify(); + assert_eq!(result, expected, "expected random value {expected:?}"); + rt.reset(); + } + + // check cache + { + let expected: [u8; 32] = rand.gen(); + rt.expect_get_randomness_from_beacon( + fil_actors_runtime::runtime::DomainSeparationTag::EvmPrevRandao, + 101, + b"prevrandao".to_vec(), + expected, + ); + let expected = [expected, expected].concat(); + + let result = util::invoke_contract(&mut rt, &dispatch_num_word(1)); + rt.verify(); + assert_eq!(result, expected, "expected 2 of the same random value {expected:?}"); + rt.reset(); + } +} diff --git a/actors/evm/tests/contracts/CallActorPrecompile.abi b/actors/evm/tests/contracts/CallActorPrecompile.abi new file mode 100644 index 000000000..c503fef76 --- /dev/null +++ b/actors/evm/tests/contracts/CallActorPrecompile.abi @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"uint64","name":"method","type":"uint64"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint64","name":"flags","type":"uint64"},{"internalType":"uint64","name":"codec","type":"uint64"},{"internalType":"bytes","name":"params","type":"bytes"},{"internalType":"bytes","name":"filAddress","type":"bytes"}],"name":"call_actor_address","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"int256","name":"","type":"int256"},{"internalType":"uint64","name":"","type":"uint64"},{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"method","type":"uint64"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint64","name":"flags","type":"uint64"},{"internalType":"uint64","name":"codec","type":"uint64"},{"internalType":"bytes","name":"params","type":"bytes"},{"internalType":"uint64","name":"id","type":"uint64"}],"name":"call_actor_id","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"int256","name":"","type":"int256"},{"internalType":"uint64","name":"","type":"uint64"},{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/actors/evm/tests/contracts/CallActorPrecompile.bin b/actors/evm/tests/contracts/CallActorPrecompile.bin new file mode 100644 index 000000000..0384970a0 --- /dev/null +++ b/actors/evm/tests/contracts/CallActorPrecompile.bin @@ -0,0 +1 @@ +608060405234801561001057600080fd5b50610994806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80636aba510b1461003b5780637f18c2201461006e575b600080fd5b61005560048036038101906100509190610375565b6100a1565b60405161006594939291906104f7565b60405180910390f35b61008860048036038101906100839190610543565b610192565b60405161009894939291906104f7565b60405180910390f35b6000806000606060008073fe0000000000000000000000000000000000000573ffffffffffffffffffffffffffffffffffffffff168d8d8d8d8d8d8d6040516020016100f3979695949392919061065d565b60405160208183030381529060405260405161010f9190610703565b600060405180830381855af49150503d806000811461014a576040519150601f19603f3d011682016040523d82523d6000602084013e61014f565b606091505b509150915060008060008380602001905181019061016d919061087c565b9250925092508483838398509850985098505050505050975097509750979350505050565b6000806000606060008073fe0000000000000000000000000000000000000373ffffffffffffffffffffffffffffffffffffffff168e8e8e8e8e8e8e8e6040516020016101e69897969594939291906108eb565b6040516020818303038152906040526040516102029190610703565b600060405180830381855af49150503d806000811461023d576040519150601f19603f3d011682016040523d82523d6000602084013e610242565b606091505b5091509150600080600083806020019051810190610260919061087c565b925092509250848383839850985098509850505050505098509850985098945050505050565b6000604051905090565b600080fd5b600080fd5b600067ffffffffffffffff82169050919050565b6102b78161029a565b81146102c257600080fd5b50565b6000813590506102d4816102ae565b92915050565b6000819050919050565b6102ed816102da565b81146102f857600080fd5b50565b60008135905061030a816102e4565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f84011261033557610334610310565b5b8235905067ffffffffffffffff81111561035257610351610315565b5b60208301915083600182028301111561036e5761036d61031a565b5b9250929050565b600080600080600080600060c0888a03121561039457610393610290565b5b60006103a28a828b016102c5565b97505060206103b38a828b016102fb565b96505060406103c48a828b016102c5565b95505060606103d58a828b016102c5565b945050608088013567ffffffffffffffff8111156103f6576103f5610295565b5b6104028a828b0161031f565b935093505060a06104158a828b016102c5565b91505092959891949750929550565b60008115159050919050565b61043981610424565b82525050565b6000819050919050565b6104528161043f565b82525050565b6104618161029a565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b838110156104a1578082015181840152602081019050610486565b60008484015250505050565b6000601f19601f8301169050919050565b60006104c982610467565b6104d38185610472565b93506104e3818560208601610483565b6104ec816104ad565b840191505092915050565b600060808201905061050c6000830187610430565b6105196020830186610449565b6105266040830185610458565b818103606083015261053881846104be565b905095945050505050565b60008060008060008060008060c0898b03121561056357610562610290565b5b60006105718b828c016102c5565b98505060206105828b828c016102fb565b97505060406105938b828c016102c5565b96505060606105a48b828c016102c5565b955050608089013567ffffffffffffffff8111156105c5576105c4610295565b5b6105d18b828c0161031f565b945094505060a089013567ffffffffffffffff8111156105f4576105f3610295565b5b6106008b828c0161031f565b92509250509295985092959890939650565b61061b816102da565b82525050565b82818337600083830152505050565b600061063c8385610472565b9350610649838584610621565b610652836104ad565b840190509392505050565b600060c082019050610672600083018a610458565b61067f6020830189610612565b61068c6040830188610458565b6106996060830187610458565b81810360808301526106ac818587610630565b90506106bb60a0830184610458565b98975050505050505050565b600081905092915050565b60006106dd82610467565b6106e781856106c7565b93506106f7818560208601610483565b80840191505092915050565b600061070f82846106d2565b915081905092915050565b6107238161043f565b811461072e57600080fd5b50565b6000815190506107408161071a565b92915050565b600081519050610755816102ae565b92915050565b600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b610798826104ad565b810181811067ffffffffffffffff821117156107b7576107b6610760565b5b80604052505050565b60006107ca610286565b90506107d6828261078f565b919050565b600067ffffffffffffffff8211156107f6576107f5610760565b5b6107ff826104ad565b9050602081019050919050565b600061081f61081a846107db565b6107c0565b90508281526020810184848401111561083b5761083a61075b565b5b610846848285610483565b509392505050565b600082601f83011261086357610862610310565b5b815161087384826020860161080c565b91505092915050565b60008060006060848603121561089557610894610290565b5b60006108a386828701610731565b93505060206108b486828701610746565b925050604084015167ffffffffffffffff8111156108d5576108d4610295565b5b6108e18682870161084e565b9150509250925092565b600060c082019050610900600083018b610458565b61090d602083018a610612565b61091a6040830189610458565b6109276060830188610458565b818103608083015261093a818688610630565b905081810360a083015261094f818486610630565b9050999850505050505050505056fea2646970667358221220515f7933bf6da59fa92065c38d853b75e241eda832bbaccdf9a8ca357aa6167964736f6c63430008110033 \ No newline at end of file diff --git a/actors/evm/tests/contracts/CallActorPrecompile.hex b/actors/evm/tests/contracts/CallActorPrecompile.hex new file mode 100644 index 000000000..0384970a0 --- /dev/null +++ b/actors/evm/tests/contracts/CallActorPrecompile.hex @@ -0,0 +1 @@ +608060405234801561001057600080fd5b50610994806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80636aba510b1461003b5780637f18c2201461006e575b600080fd5b61005560048036038101906100509190610375565b6100a1565b60405161006594939291906104f7565b60405180910390f35b61008860048036038101906100839190610543565b610192565b60405161009894939291906104f7565b60405180910390f35b6000806000606060008073fe0000000000000000000000000000000000000573ffffffffffffffffffffffffffffffffffffffff168d8d8d8d8d8d8d6040516020016100f3979695949392919061065d565b60405160208183030381529060405260405161010f9190610703565b600060405180830381855af49150503d806000811461014a576040519150601f19603f3d011682016040523d82523d6000602084013e61014f565b606091505b509150915060008060008380602001905181019061016d919061087c565b9250925092508483838398509850985098505050505050975097509750979350505050565b6000806000606060008073fe0000000000000000000000000000000000000373ffffffffffffffffffffffffffffffffffffffff168e8e8e8e8e8e8e8e6040516020016101e69897969594939291906108eb565b6040516020818303038152906040526040516102029190610703565b600060405180830381855af49150503d806000811461023d576040519150601f19603f3d011682016040523d82523d6000602084013e610242565b606091505b5091509150600080600083806020019051810190610260919061087c565b925092509250848383839850985098509850505050505098509850985098945050505050565b6000604051905090565b600080fd5b600080fd5b600067ffffffffffffffff82169050919050565b6102b78161029a565b81146102c257600080fd5b50565b6000813590506102d4816102ae565b92915050565b6000819050919050565b6102ed816102da565b81146102f857600080fd5b50565b60008135905061030a816102e4565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f84011261033557610334610310565b5b8235905067ffffffffffffffff81111561035257610351610315565b5b60208301915083600182028301111561036e5761036d61031a565b5b9250929050565b600080600080600080600060c0888a03121561039457610393610290565b5b60006103a28a828b016102c5565b97505060206103b38a828b016102fb565b96505060406103c48a828b016102c5565b95505060606103d58a828b016102c5565b945050608088013567ffffffffffffffff8111156103f6576103f5610295565b5b6104028a828b0161031f565b935093505060a06104158a828b016102c5565b91505092959891949750929550565b60008115159050919050565b61043981610424565b82525050565b6000819050919050565b6104528161043f565b82525050565b6104618161029a565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b838110156104a1578082015181840152602081019050610486565b60008484015250505050565b6000601f19601f8301169050919050565b60006104c982610467565b6104d38185610472565b93506104e3818560208601610483565b6104ec816104ad565b840191505092915050565b600060808201905061050c6000830187610430565b6105196020830186610449565b6105266040830185610458565b818103606083015261053881846104be565b905095945050505050565b60008060008060008060008060c0898b03121561056357610562610290565b5b60006105718b828c016102c5565b98505060206105828b828c016102fb565b97505060406105938b828c016102c5565b96505060606105a48b828c016102c5565b955050608089013567ffffffffffffffff8111156105c5576105c4610295565b5b6105d18b828c0161031f565b945094505060a089013567ffffffffffffffff8111156105f4576105f3610295565b5b6106008b828c0161031f565b92509250509295985092959890939650565b61061b816102da565b82525050565b82818337600083830152505050565b600061063c8385610472565b9350610649838584610621565b610652836104ad565b840190509392505050565b600060c082019050610672600083018a610458565b61067f6020830189610612565b61068c6040830188610458565b6106996060830187610458565b81810360808301526106ac818587610630565b90506106bb60a0830184610458565b98975050505050505050565b600081905092915050565b60006106dd82610467565b6106e781856106c7565b93506106f7818560208601610483565b80840191505092915050565b600061070f82846106d2565b915081905092915050565b6107238161043f565b811461072e57600080fd5b50565b6000815190506107408161071a565b92915050565b600081519050610755816102ae565b92915050565b600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b610798826104ad565b810181811067ffffffffffffffff821117156107b7576107b6610760565b5b80604052505050565b60006107ca610286565b90506107d6828261078f565b919050565b600067ffffffffffffffff8211156107f6576107f5610760565b5b6107ff826104ad565b9050602081019050919050565b600061081f61081a846107db565b6107c0565b90508281526020810184848401111561083b5761083a61075b565b5b610846848285610483565b509392505050565b600082601f83011261086357610862610310565b5b815161087384826020860161080c565b91505092915050565b60008060006060848603121561089557610894610290565b5b60006108a386828701610731565b93505060206108b486828701610746565b925050604084015167ffffffffffffffff8111156108d5576108d4610295565b5b6108e18682870161084e565b9150509250925092565b600060c082019050610900600083018b610458565b61090d602083018a610612565b61091a6040830189610458565b6109276060830188610458565b818103608083015261093a818688610630565b905081810360a083015261094f818486610630565b9050999850505050505050505056fea2646970667358221220515f7933bf6da59fa92065c38d853b75e241eda832bbaccdf9a8ca357aa6167964736f6c63430008110033 \ No newline at end of file diff --git a/actors/evm/tests/contracts/CallActorPrecompile.signatures b/actors/evm/tests/contracts/CallActorPrecompile.signatures new file mode 100644 index 000000000..3a5271ecf --- /dev/null +++ b/actors/evm/tests/contracts/CallActorPrecompile.signatures @@ -0,0 +1,3 @@ +Function signatures: +7f18c220: call_actor_address(uint64,uint256,uint64,uint64,bytes,bytes) +6aba510b: call_actor_id(uint64,uint256,uint64,uint64,bytes,uint64) diff --git a/actors/evm/tests/contracts/CallActorPrecompile.sol b/actors/evm/tests/contracts/CallActorPrecompile.sol new file mode 100644 index 000000000..a10f604b0 --- /dev/null +++ b/actors/evm/tests/contracts/CallActorPrecompile.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: Apache-2.0 MIT +pragma solidity >=0.4.25 <=0.8.17; + +contract CallActorPrecompile { + address constant CALL_ACTOR_ADDRESS = 0xfe00000000000000000000000000000000000003; + address constant CALL_ACTOR_ID = 0xfe00000000000000000000000000000000000005; + + function call_actor_id(uint64 method, uint256 value, uint64 flags, uint64 codec, bytes calldata params, uint64 id) public returns (bool, int256, uint64, bytes memory) { + (bool success, bytes memory data) = address(CALL_ACTOR_ID).delegatecall(abi.encode(method, value, flags, codec, params, id)); + (int256 exit, uint64 return_codec, bytes memory return_value) = abi.decode(data, (int256, uint64, bytes)); + return (success, exit, return_codec, return_value); + } + + function call_actor_address(uint64 method, uint256 value, uint64 flags, uint64 codec, bytes calldata params, bytes calldata filAddress) public returns (bool, int256, uint64, bytes memory) { + (bool success, bytes memory data) = address(CALL_ACTOR_ADDRESS).delegatecall(abi.encode(method, value, flags, codec, params, filAddress)); + (int256 exit, uint64 return_codec, bytes memory return_value) = abi.decode(data, (int256, uint64, bytes)); + return (success, exit, return_codec, return_value); + } +} diff --git a/actors/evm/tests/contracts/CallActorPrecompile_storage.json b/actors/evm/tests/contracts/CallActorPrecompile_storage.json new file mode 100644 index 000000000..325d0043f --- /dev/null +++ b/actors/evm/tests/contracts/CallActorPrecompile_storage.json @@ -0,0 +1 @@ +{"storage":[]} \ No newline at end of file diff --git a/actors/evm/tests/contracts/Factory.abi b/actors/evm/tests/contracts/Factory.abi new file mode 100644 index 000000000..c3a543181 --- /dev/null +++ b/actors/evm/tests/contracts/Factory.abi @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"int32","name":"value","type":"int32"}],"name":"create","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"int32","name":"value","type":"int32"}],"name":"create2","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/actors/evm/tests/contracts/Factory.bin b/actors/evm/tests/contracts/Factory.bin new file mode 100644 index 000000000..4b53a414b --- /dev/null +++ b/actors/evm/tests/contracts/Factory.bin @@ -0,0 +1 @@ +608060405234801561001057600080fd5b5061048a806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80630876a11e1461003b5780631e1932941461006b575b600080fd5b6100556004803603810190610050919061019c565b61009b565b604051610062919061021d565b60405180910390f35b61008560048036038101906100809190610238565b6100de565b604051610092919061021d565b60405180910390f35b600082826040516100ab9061011b565b6100b59190610274565b8190604051809103906000f59050801580156100d5573d6000803e3d6000fd5b50905092915050565b6000816040516100ed9061011b565b6100f79190610274565b604051809103906000f080158015610113573d6000803e3d6000fd5b509050919050565b6101c58061029083390190565b600080fd5b6000819050919050565b6101408161012d565b811461014b57600080fd5b50565b60008135905061015d81610137565b92915050565b60008160030b9050919050565b61017981610163565b811461018457600080fd5b50565b60008135905061019681610170565b92915050565b600080604083850312156101b3576101b2610128565b5b60006101c18582860161014e565b92505060206101d285828601610187565b9150509250929050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610207826101dc565b9050919050565b610217816101fc565b82525050565b6000602082019050610232600083018461020e565b92915050565b60006020828403121561024e5761024d610128565b5b600061025c84828501610187565b91505092915050565b61026e81610163565b82525050565b60006020820190506102896000830184610265565b9291505056fe608060405234801561001057600080fd5b506040516101c53803806101c583398181016040528101906100329190610099565b806000806101000a81548163ffffffff021916908360030b63ffffffff160217905550506100c6565b600080fd5b60008160030b9050919050565b61007681610060565b811461008157600080fd5b50565b6000815190506100938161006d565b92915050565b6000602082840312156100af576100ae61005b565b5b60006100bd84828501610084565b91505092915050565b60f1806100d46000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c806333cf508014603757806335f46994146051575b600080fd5b603d6059565b6040516048919060a2565b60405180910390f35b6057606f565b005b60008060009054906101000a900460030b905090565b3373ffffffffffffffffffffffffffffffffffffffff16ff5b60008160030b9050919050565b609c816088565b82525050565b600060208201905060b560008301846095565b9291505056fea2646970667358221220515fddaf42d9a26595aaf38ed0775a03d845e2d847b5e94a23ec84f414bea90e64736f6c63430008110033a264697066735822122060b27608659f19cc27f02c0145ae6714d9886ccd9a56178f95522973c86d813864736f6c63430008110033 \ No newline at end of file diff --git a/actors/evm/tests/contracts/Factory.signatures b/actors/evm/tests/contracts/Factory.signatures new file mode 100644 index 000000000..2f4b9f22f --- /dev/null +++ b/actors/evm/tests/contracts/Factory.signatures @@ -0,0 +1,3 @@ +Function signatures: +1e193294: create(int32) +0876a11e: create2(bytes32,int32) diff --git a/actors/evm/tests/contracts/FactoryChild.abi b/actors/evm/tests/contracts/FactoryChild.abi new file mode 100644 index 000000000..dfc27b77d --- /dev/null +++ b/actors/evm/tests/contracts/FactoryChild.abi @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"int32","name":"arg","type":"int32"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"die","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"get_value","outputs":[{"internalType":"int32","name":"","type":"int32"}],"stateMutability":"view","type":"function"}] \ No newline at end of file diff --git a/actors/evm/tests/contracts/FactoryChild.bin b/actors/evm/tests/contracts/FactoryChild.bin new file mode 100644 index 000000000..763e5e2c6 --- /dev/null +++ b/actors/evm/tests/contracts/FactoryChild.bin @@ -0,0 +1 @@ +608060405234801561001057600080fd5b506040516101c53803806101c583398181016040528101906100329190610099565b806000806101000a81548163ffffffff021916908360030b63ffffffff160217905550506100c6565b600080fd5b60008160030b9050919050565b61007681610060565b811461008157600080fd5b50565b6000815190506100938161006d565b92915050565b6000602082840312156100af576100ae61005b565b5b60006100bd84828501610084565b91505092915050565b60f1806100d46000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c806333cf508014603757806335f46994146051575b600080fd5b603d6059565b6040516048919060a2565b60405180910390f35b6057606f565b005b60008060009054906101000a900460030b905090565b3373ffffffffffffffffffffffffffffffffffffffff16ff5b60008160030b9050919050565b609c816088565b82525050565b600060208201905060b560008301846095565b9291505056fea2646970667358221220515fddaf42d9a26595aaf38ed0775a03d845e2d847b5e94a23ec84f414bea90e64736f6c63430008110033 \ No newline at end of file diff --git a/actors/evm/tests/contracts/FactoryChild.signatures b/actors/evm/tests/contracts/FactoryChild.signatures new file mode 100644 index 000000000..1e94befa6 --- /dev/null +++ b/actors/evm/tests/contracts/FactoryChild.signatures @@ -0,0 +1,3 @@ +Function signatures: +35f46994: die() +33cf5080: get_value() diff --git a/actors/evm/tests/contracts/FactoryChild_storage.json b/actors/evm/tests/contracts/FactoryChild_storage.json new file mode 100644 index 000000000..04f8a0f00 --- /dev/null +++ b/actors/evm/tests/contracts/FactoryChild_storage.json @@ -0,0 +1 @@ +{"storage":[{"astId":42,"contract":"tests/contracts/Lifecycle.sol:FactoryChild","label":"value","offset":0,"slot":"0","type":"t_int32"}],"types":{"t_int32":{"encoding":"inplace","label":"int32","numberOfBytes":"4"}}} \ No newline at end of file diff --git a/actors/evm/tests/contracts/Factory_storage.json b/actors/evm/tests/contracts/Factory_storage.json new file mode 100644 index 000000000..325d0043f --- /dev/null +++ b/actors/evm/tests/contracts/Factory_storage.json @@ -0,0 +1 @@ +{"storage":[]} \ No newline at end of file diff --git a/actors/evm/tests/contracts/FeSimplecoin.fe b/actors/evm/tests/contracts/FeSimplecoin.fe new file mode 100644 index 000000000..ab4df6583 --- /dev/null +++ b/actors/evm/tests/contracts/FeSimplecoin.fe @@ -0,0 +1,33 @@ +struct Transfer { + #indexed + pub from: address + #indexed + pub to: address + pub value: u256 +} + +contract FeSimplecoin { + balances: Map + + pub fn __init__(mut self, ctx: Context) { + self.balances[ctx.tx_origin()] = 10000; + } + + pub fn sendCoin(mut self, mut ctx: Context, receiver: address, amount: u256) -> bool { + if self.balances[ctx.msg_sender()] < amount { + return false + } + self.balances[ctx.msg_sender()] -= amount; + self.balances[receiver] += amount; + ctx.emit(Transfer(from: ctx.msg_sender(), to: receiver, value: amount)); + return true + } + + pub fn getBalanceInEth(self, addr: address) -> u256 { + return self.getBalance(addr) * 2; + } + + pub fn getBalance(self, addr: address) -> u256 { + return self.balances[addr]; + } +} \ No newline at end of file diff --git a/actors/evm/tests/contracts/FilecoinFallback.abi b/actors/evm/tests/contracts/FilecoinFallback.abi new file mode 100644 index 000000000..bc62a9811 --- /dev/null +++ b/actors/evm/tests/contracts/FilecoinFallback.abi @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"uint64","name":"method","type":"uint64"},{"internalType":"uint64","name":"codec","type":"uint64"},{"internalType":"bytes","name":"params","type":"bytes"}],"name":"handle_filecoin_method","outputs":[{"internalType":"uint32","name":"","type":"uint32"},{"internalType":"uint64","name":"","type":"uint64"},{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"pure","type":"function"}] \ No newline at end of file diff --git a/actors/evm/tests/contracts/FilecoinFallback.bin b/actors/evm/tests/contracts/FilecoinFallback.bin new file mode 100644 index 000000000..636dfcbe9 --- /dev/null +++ b/actors/evm/tests/contracts/FilecoinFallback.bin @@ -0,0 +1 @@ +608060405234801561001057600080fd5b50610401806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063868e10c414610030575b600080fd5b61004a6004803603810190610045919061025b565b610062565b6040516100599392919061038d565b60405180910390f35b600080606060008585905014151560008767ffffffffffffffff161415151461008a57600080fd5b6104008767ffffffffffffffff16036100bb57600080604051806020016040528060008152509250925092506101a2565b6104018767ffffffffffffffff160361011357600060516040518060400160405280600681526020017f666f6f62617200000000000000000000000000000000000000000000000000008152509250925092506101a2565b6104028767ffffffffffffffff160361014557602a6000604051806020016040528060008152509250925092506101a2565b6104038767ffffffffffffffff160361019d57602a60516040518060400160405280600681526020017f666f6f62617200000000000000000000000000000000000000000000000000008152509250925092506101a2565b600080fd5b9450945094915050565b600080fd5b600080fd5b600067ffffffffffffffff82169050919050565b6101d3816101b6565b81146101de57600080fd5b50565b6000813590506101f0816101ca565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f84011261021b5761021a6101f6565b5b8235905067ffffffffffffffff811115610238576102376101fb565b5b60208301915083600182028301111561025457610253610200565b5b9250929050565b60008060008060608587031215610275576102746101ac565b5b6000610283878288016101e1565b9450506020610294878288016101e1565b935050604085013567ffffffffffffffff8111156102b5576102b46101b1565b5b6102c187828801610205565b925092505092959194509250565b600063ffffffff82169050919050565b6102e8816102cf565b82525050565b6102f7816101b6565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561033757808201518184015260208101905061031c565b60008484015250505050565b6000601f19601f8301169050919050565b600061035f826102fd565b6103698185610308565b9350610379818560208601610319565b61038281610343565b840191505092915050565b60006060820190506103a260008301866102df565b6103af60208301856102ee565b81810360408301526103c18184610354565b905094935050505056fea264697066735822122032ee8363794cb126de27841436bd2a246c5014fef0a9dcf6e3059206933bd05e64736f6c63430008110033 \ No newline at end of file diff --git a/actors/evm/tests/contracts/FilecoinFallback.hex b/actors/evm/tests/contracts/FilecoinFallback.hex new file mode 100644 index 000000000..636dfcbe9 --- /dev/null +++ b/actors/evm/tests/contracts/FilecoinFallback.hex @@ -0,0 +1 @@ +608060405234801561001057600080fd5b50610401806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063868e10c414610030575b600080fd5b61004a6004803603810190610045919061025b565b610062565b6040516100599392919061038d565b60405180910390f35b600080606060008585905014151560008767ffffffffffffffff161415151461008a57600080fd5b6104008767ffffffffffffffff16036100bb57600080604051806020016040528060008152509250925092506101a2565b6104018767ffffffffffffffff160361011357600060516040518060400160405280600681526020017f666f6f62617200000000000000000000000000000000000000000000000000008152509250925092506101a2565b6104028767ffffffffffffffff160361014557602a6000604051806020016040528060008152509250925092506101a2565b6104038767ffffffffffffffff160361019d57602a60516040518060400160405280600681526020017f666f6f62617200000000000000000000000000000000000000000000000000008152509250925092506101a2565b600080fd5b9450945094915050565b600080fd5b600080fd5b600067ffffffffffffffff82169050919050565b6101d3816101b6565b81146101de57600080fd5b50565b6000813590506101f0816101ca565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f84011261021b5761021a6101f6565b5b8235905067ffffffffffffffff811115610238576102376101fb565b5b60208301915083600182028301111561025457610253610200565b5b9250929050565b60008060008060608587031215610275576102746101ac565b5b6000610283878288016101e1565b9450506020610294878288016101e1565b935050604085013567ffffffffffffffff8111156102b5576102b46101b1565b5b6102c187828801610205565b925092505092959194509250565b600063ffffffff82169050919050565b6102e8816102cf565b82525050565b6102f7816101b6565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561033757808201518184015260208101905061031c565b60008484015250505050565b6000601f19601f8301169050919050565b600061035f826102fd565b6103698185610308565b9350610379818560208601610319565b61038281610343565b840191505092915050565b60006060820190506103a260008301866102df565b6103af60208301856102ee565b81810360408301526103c18184610354565b905094935050505056fea264697066735822122032ee8363794cb126de27841436bd2a246c5014fef0a9dcf6e3059206933bd05e64736f6c63430008110033 \ No newline at end of file diff --git a/actors/evm/tests/contracts/FilecoinFallback.signatures b/actors/evm/tests/contracts/FilecoinFallback.signatures new file mode 100644 index 000000000..931c1484c --- /dev/null +++ b/actors/evm/tests/contracts/FilecoinFallback.signatures @@ -0,0 +1,2 @@ +Function signatures: +868e10c4: handle_filecoin_method(uint64,uint64,bytes) diff --git a/actors/evm/tests/contracts/FilecoinFallback.sol b/actors/evm/tests/contracts/FilecoinFallback.sol new file mode 100644 index 000000000..b6f36b54f --- /dev/null +++ b/actors/evm/tests/contracts/FilecoinFallback.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: Apache-2.0 MIT +pragma solidity >=0.4.25 <=0.8.17; + +contract FilecoinFallback { + function handle_filecoin_method(uint64 method, uint64 codec, bytes calldata params) pure public returns (uint32, uint64, bytes memory) { + require((codec == 0) == (params.length == 0)); + if (method == 1024) { + return ( 0, 0, bytes("") ); + } else if (method == 1025) { + return ( 0, 0x51, bytes("foobar") ); + } else if (method == 1026) { + return ( 42, 0, bytes("") ); + } else if (method == 1027) { + return ( 42, 0x51, bytes("foobar") ); + } + revert(); + } +} diff --git a/actors/evm/tests/contracts/FilecoinFallback_storage.json b/actors/evm/tests/contracts/FilecoinFallback_storage.json new file mode 100644 index 000000000..325d0043f --- /dev/null +++ b/actors/evm/tests/contracts/FilecoinFallback_storage.json @@ -0,0 +1 @@ +{"storage":[]} \ No newline at end of file diff --git a/actors/evm/tests/contracts/Lifecycle.hex b/actors/evm/tests/contracts/Lifecycle.hex new file mode 100644 index 000000000..4b53a414b --- /dev/null +++ b/actors/evm/tests/contracts/Lifecycle.hex @@ -0,0 +1 @@ +608060405234801561001057600080fd5b5061048a806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80630876a11e1461003b5780631e1932941461006b575b600080fd5b6100556004803603810190610050919061019c565b61009b565b604051610062919061021d565b60405180910390f35b61008560048036038101906100809190610238565b6100de565b604051610092919061021d565b60405180910390f35b600082826040516100ab9061011b565b6100b59190610274565b8190604051809103906000f59050801580156100d5573d6000803e3d6000fd5b50905092915050565b6000816040516100ed9061011b565b6100f79190610274565b604051809103906000f080158015610113573d6000803e3d6000fd5b509050919050565b6101c58061029083390190565b600080fd5b6000819050919050565b6101408161012d565b811461014b57600080fd5b50565b60008135905061015d81610137565b92915050565b60008160030b9050919050565b61017981610163565b811461018457600080fd5b50565b60008135905061019681610170565b92915050565b600080604083850312156101b3576101b2610128565b5b60006101c18582860161014e565b92505060206101d285828601610187565b9150509250929050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610207826101dc565b9050919050565b610217816101fc565b82525050565b6000602082019050610232600083018461020e565b92915050565b60006020828403121561024e5761024d610128565b5b600061025c84828501610187565b91505092915050565b61026e81610163565b82525050565b60006020820190506102896000830184610265565b9291505056fe608060405234801561001057600080fd5b506040516101c53803806101c583398181016040528101906100329190610099565b806000806101000a81548163ffffffff021916908360030b63ffffffff160217905550506100c6565b600080fd5b60008160030b9050919050565b61007681610060565b811461008157600080fd5b50565b6000815190506100938161006d565b92915050565b6000602082840312156100af576100ae61005b565b5b60006100bd84828501610084565b91505092915050565b60f1806100d46000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c806333cf508014603757806335f46994146051575b600080fd5b603d6059565b6040516048919060a2565b60405180910390f35b6057606f565b005b60008060009054906101000a900460030b905090565b3373ffffffffffffffffffffffffffffffffffffffff16ff5b60008160030b9050919050565b609c816088565b82525050565b600060208201905060b560008301846095565b9291505056fea2646970667358221220515fddaf42d9a26595aaf38ed0775a03d845e2d847b5e94a23ec84f414bea90e64736f6c63430008110033a264697066735822122060b27608659f19cc27f02c0145ae6714d9886ccd9a56178f95522973c86d813864736f6c63430008110033 \ No newline at end of file diff --git a/actors/evm/tests/contracts/Lifecycle.sol b/actors/evm/tests/contracts/Lifecycle.sol new file mode 100644 index 000000000..61ee3ad35 --- /dev/null +++ b/actors/evm/tests/contracts/Lifecycle.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: Apache-2.0 MIT +pragma solidity >=0.4.25 <=0.8.17; + +contract Factory { + function create(int32 value) public returns (address) { + return address(new FactoryChild(value)); + } + + function create2(bytes32 salt, int32 value) public returns (address) { + return address(new FactoryChild{salt: salt}(value)); + } +} + +contract FactoryChild { + int32 value; + constructor(int32 arg) { + value = arg; + } + function die() public { + selfdestruct(payable(msg.sender)); + } + function get_value() public view returns (int32) { + return value; + } +} diff --git a/actors/evm/tests/contracts/Recursive.abi b/actors/evm/tests/contracts/Recursive.abi new file mode 100644 index 000000000..22c1dd5cc --- /dev/null +++ b/actors/evm/tests/contracts/Recursive.abi @@ -0,0 +1 @@ +[{"inputs":[],"name":"enter","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"recurse","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/actors/evm/tests/contracts/Recursive.bin b/actors/evm/tests/contracts/Recursive.bin new file mode 100644 index 000000000..6838e2554 --- /dev/null +++ b/actors/evm/tests/contracts/Recursive.bin @@ -0,0 +1 @@ +608060405234801561001057600080fd5b506102ac806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063e97dcb621461003b578063ea0f66b114610059575b600080fd5b610043610077565b60405161005091906101fd565b60405180910390f35b61006161017f565b60405161006e91906101fd565b60405180910390f35b60008060009054906101000a900460ff1615610096576001905061017c565b60016000806101000a81548160ff02191690831515021790555060003073ffffffffffffffffffffffffffffffffffffffff1663ea0f66b16040518163ffffffff1660e01b81526004016020604051808303816000875af11580156100ff573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101239190610249565b905060008163ffffffff161461013c578091505061017c565b60008054906101000a900460ff1661015857600491505061017c565b600060019054906101000a900460ff1661017657600591505061017c565b60009150505b90565b60008060009054906101000a900460ff1661019d57600290506101db565b600060019054906101000a900460ff16156101bb57600390506101db565b6001600060016101000a81548160ff021916908315150217905550600090505b90565b600063ffffffff82169050919050565b6101f7816101de565b82525050565b600060208201905061021260008301846101ee565b92915050565b600080fd5b610226816101de565b811461023157600080fd5b50565b6000815190506102438161021d565b92915050565b60006020828403121561025f5761025e610218565b5b600061026d84828501610234565b9150509291505056fea264697066735822122048dc6be27b10b6aea4c61982bf8f8c3cd96348d729cc42a0daf77b2a2675d9f464736f6c63430008110033 \ No newline at end of file diff --git a/actors/evm/tests/contracts/Recursive.hex b/actors/evm/tests/contracts/Recursive.hex new file mode 100644 index 000000000..6838e2554 --- /dev/null +++ b/actors/evm/tests/contracts/Recursive.hex @@ -0,0 +1 @@ +608060405234801561001057600080fd5b506102ac806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063e97dcb621461003b578063ea0f66b114610059575b600080fd5b610043610077565b60405161005091906101fd565b60405180910390f35b61006161017f565b60405161006e91906101fd565b60405180910390f35b60008060009054906101000a900460ff1615610096576001905061017c565b60016000806101000a81548160ff02191690831515021790555060003073ffffffffffffffffffffffffffffffffffffffff1663ea0f66b16040518163ffffffff1660e01b81526004016020604051808303816000875af11580156100ff573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101239190610249565b905060008163ffffffff161461013c578091505061017c565b60008054906101000a900460ff1661015857600491505061017c565b600060019054906101000a900460ff1661017657600591505061017c565b60009150505b90565b60008060009054906101000a900460ff1661019d57600290506101db565b600060019054906101000a900460ff16156101bb57600390506101db565b6001600060016101000a81548160ff021916908315150217905550600090505b90565b600063ffffffff82169050919050565b6101f7816101de565b82525050565b600060208201905061021260008301846101ee565b92915050565b600080fd5b610226816101de565b811461023157600080fd5b50565b6000815190506102438161021d565b92915050565b60006020828403121561025f5761025e610218565b5b600061026d84828501610234565b9150509291505056fea264697066735822122048dc6be27b10b6aea4c61982bf8f8c3cd96348d729cc42a0daf77b2a2675d9f464736f6c63430008110033 \ No newline at end of file diff --git a/actors/evm/tests/contracts/Recursive.signatures b/actors/evm/tests/contracts/Recursive.signatures new file mode 100644 index 000000000..ca517915c --- /dev/null +++ b/actors/evm/tests/contracts/Recursive.signatures @@ -0,0 +1,3 @@ +Function signatures: +e97dcb62: enter() +ea0f66b1: recurse() diff --git a/actors/evm/tests/contracts/Recursive.sol b/actors/evm/tests/contracts/Recursive.sol new file mode 100644 index 000000000..22de0acf4 --- /dev/null +++ b/actors/evm/tests/contracts/Recursive.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: Apache-2.0 MIT +pragma solidity >=0.4.25 <=0.8.17; + +contract Recursive { + bool a; + bool b; + + function enter() public returns (uint32) { + if (a) { + return 1; + } + a = true; + uint32 result = Recursive(address(this)).recurse(); + if (result != 0) { + return result; + } + + if (!a) { + return 4; + } + + if (!b) { + return 5; + } + return 0; + } + + function recurse() public returns (uint32) { + if (!a) { + return 2; + } + if (b) { + return 3; + } + b = true; + return 0; + } +} diff --git a/actors/evm/tests/contracts/Recursive_storage.json b/actors/evm/tests/contracts/Recursive_storage.json new file mode 100644 index 000000000..c173d7aef --- /dev/null +++ b/actors/evm/tests/contracts/Recursive_storage.json @@ -0,0 +1 @@ +{"storage":[{"astId":3,"contract":"tests/contracts/Recursive.sol:Recursive","label":"a","offset":0,"slot":"0","type":"t_bool"},{"astId":5,"contract":"tests/contracts/Recursive.sol:Recursive","label":"b","offset":1,"slot":"0","type":"t_bool"}],"types":{"t_bool":{"encoding":"inplace","label":"bool","numberOfBytes":"1"}}} \ No newline at end of file diff --git a/actors/evm/tests/contracts/Selfdestruct.abi b/actors/evm/tests/contracts/Selfdestruct.abi new file mode 100644 index 000000000..8ed3d9ccd --- /dev/null +++ b/actors/evm/tests/contracts/Selfdestruct.abi @@ -0,0 +1 @@ +[{"inputs":[],"name":"die","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"one","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"}] \ No newline at end of file diff --git a/actors/evm/tests/contracts/Selfdestruct.bin b/actors/evm/tests/contracts/Selfdestruct.bin new file mode 100644 index 000000000..fd4eaf0c4 --- /dev/null +++ b/actors/evm/tests/contracts/Selfdestruct.bin @@ -0,0 +1 @@ +608060405234801561001057600080fd5b5060f58061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c806335f46994146037578063901717d114603f575b600080fd5b603d6059565b005b60456086565b6040516050919060a6565b60405180910390f35b73ff000000000000000000000000000000000003e973ffffffffffffffffffffffffffffffffffffffff16ff5b60006001905090565b6000819050919050565b60a081608f565b82525050565b600060208201905060b960008301846099565b9291505056fea26469706673582212205e642139367a73faa34d4286b3d045c006891ee95bed3ffc39e14604bc9c3a7d64736f6c63430008110033 \ No newline at end of file diff --git a/actors/evm/tests/contracts/Selfdestruct.signatures b/actors/evm/tests/contracts/Selfdestruct.signatures new file mode 100644 index 000000000..e91c1e851 --- /dev/null +++ b/actors/evm/tests/contracts/Selfdestruct.signatures @@ -0,0 +1,3 @@ +Function signatures: +35f46994: die() +901717d1: one() diff --git a/actors/evm/tests/contracts/Selfdestruct_storage.json b/actors/evm/tests/contracts/Selfdestruct_storage.json new file mode 100644 index 000000000..ed90c78fe --- /dev/null +++ b/actors/evm/tests/contracts/Selfdestruct_storage.json @@ -0,0 +1 @@ +{"storage":[{"astId":3,"contract":"tests/contracts/Lifecycle.sol:SelfDestruct","label":"value","offset":0,"slot":"0","type":"t_int32"}],"types":{"t_int32":{"encoding":"inplace","label":"int32","numberOfBytes":"4"}}} \ No newline at end of file diff --git a/actors/evm/tests/contracts/SimpleCoin.abi b/actors/evm/tests/contracts/SimpleCoin.abi new file mode 100644 index 000000000..19482937e --- /dev/null +++ b/actors/evm/tests/contracts/SimpleCoin.abi @@ -0,0 +1 @@ +[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_from","type":"address"},{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"getBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"getBalanceInEth","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"sendCoin","outputs":[{"internalType":"bool","name":"sufficient","type":"bool"}],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/actors/evm/tests/contracts/SimpleCoin.bin b/actors/evm/tests/contracts/SimpleCoin.bin new file mode 100644 index 000000000..55b83cb12 --- /dev/null +++ b/actors/evm/tests/contracts/SimpleCoin.bin @@ -0,0 +1 @@ +608060405234801561001057600080fd5b506127106000803273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555061051c806100656000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80637bd703e81461004657806390b98a1114610076578063f8b2cb4f146100a6575b600080fd5b610060600480360381019061005b919061030a565b6100d6565b60405161006d9190610350565b60405180910390f35b610090600480360381019061008b9190610397565b6100f4565b60405161009d91906103f2565b60405180910390f35b6100c060048036038101906100bb919061030a565b61025f565b6040516100cd9190610350565b60405180910390f35b600060026100e38361025f565b6100ed919061043c565b9050919050565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410156101455760009050610259565b816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254610193919061047e565b92505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546101e891906104b2565b925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161024c9190610350565b60405180910390a3600190505b92915050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006102d7826102ac565b9050919050565b6102e7816102cc565b81146102f257600080fd5b50565b600081359050610304816102de565b92915050565b6000602082840312156103205761031f6102a7565b5b600061032e848285016102f5565b91505092915050565b6000819050919050565b61034a81610337565b82525050565b60006020820190506103656000830184610341565b92915050565b61037481610337565b811461037f57600080fd5b50565b6000813590506103918161036b565b92915050565b600080604083850312156103ae576103ad6102a7565b5b60006103bc858286016102f5565b92505060206103cd85828601610382565b9150509250929050565b60008115159050919050565b6103ec816103d7565b82525050565b600060208201905061040760008301846103e3565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061044782610337565b915061045283610337565b925082820261046081610337565b915082820484148315176104775761047661040d565b5b5092915050565b600061048982610337565b915061049483610337565b92508282039050818111156104ac576104ab61040d565b5b92915050565b60006104bd82610337565b91506104c883610337565b92508282019050808211156104e0576104df61040d565b5b9291505056fea26469706673582212205ede41ff9072784ccc19ac18de0781558d305a8139361fa85dc51a8614e47d8c64736f6c63430008110033 \ No newline at end of file diff --git a/actors/evm/tests/contracts/SimpleCoin.signatures b/actors/evm/tests/contracts/SimpleCoin.signatures new file mode 100644 index 000000000..559ecdf53 --- /dev/null +++ b/actors/evm/tests/contracts/SimpleCoin.signatures @@ -0,0 +1,7 @@ +Function signatures: +f8b2cb4f: getBalance(address) +7bd703e8: getBalanceInEth(address) +90b98a11: sendCoin(address,uint256) + +Event signatures: +ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef: Transfer(address,address,uint256) diff --git a/actors/evm/tests/contracts/SimpleCoin_storage.json b/actors/evm/tests/contracts/SimpleCoin_storage.json new file mode 100644 index 000000000..c2e0984e5 --- /dev/null +++ b/actors/evm/tests/contracts/SimpleCoin_storage.json @@ -0,0 +1 @@ +{"storage":[{"astId":5,"contract":"tests/contracts/simplecoin.sol:SimpleCoin","label":"balances","offset":0,"slot":"0","type":"t_mapping(t_address,t_uint256)"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_mapping(t_address,t_uint256)":{"encoding":"mapping","key":"t_address","label":"mapping(address => uint256)","numberOfBytes":"32","value":"t_uint256"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}} \ No newline at end of file diff --git a/actors/evm/tests/contracts/StorageFootprint.abi b/actors/evm/tests/contracts/StorageFootprint.abi new file mode 100644 index 000000000..da3b13d14 --- /dev/null +++ b/actors/evm/tests/contracts/StorageFootprint.abi @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"uint32","name":"n","type":"uint32"}],"name":"array1_push","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"k","type":"uint32"},{"internalType":"uint32","name":"n","type":"uint32"}],"name":"array1_sum","outputs":[{"internalType":"uint32","name":"sum","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"n","type":"uint32"}],"name":"array2_push","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"inc_counter1","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"inc_counters","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"k","type":"uint32"},{"internalType":"uint32","name":"n","type":"uint32"},{"internalType":"uint32","name":"v","type":"uint32"}],"name":"mapping1_set","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"k","type":"uint32"},{"internalType":"uint32","name":"n","type":"uint32"}],"name":"mapping1_sum","outputs":[{"internalType":"uint32","name":"sum","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"k","type":"uint32"},{"internalType":"uint32","name":"n","type":"uint32"},{"internalType":"uint32","name":"v","type":"uint32"}],"name":"mapping2_set","outputs":[],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/actors/evm/tests/contracts/StorageFootprint.bin b/actors/evm/tests/contracts/StorageFootprint.bin new file mode 100644 index 000000000..c382c241d --- /dev/null +++ b/actors/evm/tests/contracts/StorageFootprint.bin @@ -0,0 +1 @@ +608060405234801561001057600080fd5b506107d6806100206000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c806380a5fa431161005b57806380a5fa43146100d95780639ab3c8bb14610109578063aa835c4814610125578063ea437a3a1461014157610088565b806302c6564b1461008d57806327e561f6146100a9578063339f8645146100b357806373ef151a146100cf575b600080fd5b6100a760048036038101906100a291906105f4565b610171565b005b6100b16101f0565b005b6100cd60048036038101906100c89190610621565b6102f1565b005b6100d761036d565b005b6100f360048036038101906100ee9190610674565b6103ae565b60405161010091906106c3565b60405180910390f35b610123600480360381019061011e9190610621565b61043b565b005b61013f600480360381019061013a91906105f4565b6104b7565b005b61015b60048036038101906101569190610674565b610536565b60405161016891906106c3565b60405180910390f35b6000600190505b8163ffffffff168163ffffffff16116101ec5760018190806001815401808255809150506001900390600052602060002090600891828204019190066004029091909190916101000a81548163ffffffff021916908363ffffffff16021790555080806101e49061070d565b915050610178565b5050565b60016000808282829054906101000a900463ffffffff166102119190610739565b92506101000a81548163ffffffff021916908363ffffffff1602179055506001600060048282829054906101000a900463ffffffff166102519190610739565b92506101000a81548163ffffffff021916908363ffffffff1602179055506001600060088282829054906101000a900463ffffffff166102919190610739565b92506101000a81548163ffffffff021916908363ffffffff16021790555060016000600c8282829054906101000a900463ffffffff166102d19190610739565b92506101000a81548163ffffffff021916908363ffffffff160217905550565b60008390505b82846103039190610739565b63ffffffff168163ffffffff1610156103675781600460008363ffffffff1663ffffffff16815260200190815260200160002060006101000a81548163ffffffff021916908363ffffffff160217905550808061035f9061070d565b9150506102f7565b50505050565b60016000808282829054906101000a900463ffffffff1661038e9190610739565b92506101000a81548163ffffffff021916908363ffffffff160217905550565b6000808390505b82846103c19190610739565b63ffffffff168163ffffffff1610156104345760018163ffffffff16815481106103ee576103ed610771565b5b90600052602060002090600891828204019190066004029054906101000a900463ffffffff168261041f9190610739565b9150808061042c9061070d565b9150506103b5565b5092915050565b60008390505b828461044d9190610739565b63ffffffff168163ffffffff1610156104b15781600360008363ffffffff1663ffffffff16815260200190815260200160002060006101000a81548163ffffffff021916908363ffffffff16021790555080806104a99061070d565b915050610441565b50505050565b6000600190505b8163ffffffff168163ffffffff16116105325760028190806001815401808255809150506001900390600052602060002090600891828204019190066004029091909190916101000a81548163ffffffff021916908363ffffffff160217905550808061052a9061070d565b9150506104be565b5050565b6000808390505b82846105499190610739565b63ffffffff168163ffffffff1610156105ac57600360008263ffffffff1663ffffffff16815260200190815260200160002060009054906101000a900463ffffffff16826105979190610739565b915080806105a49061070d565b91505061053d565b5092915050565b600080fd5b600063ffffffff82169050919050565b6105d1816105b8565b81146105dc57600080fd5b50565b6000813590506105ee816105c8565b92915050565b60006020828403121561060a576106096105b3565b5b6000610618848285016105df565b91505092915050565b60008060006060848603121561063a576106396105b3565b5b6000610648868287016105df565b9350506020610659868287016105df565b925050604061066a868287016105df565b9150509250925092565b6000806040838503121561068b5761068a6105b3565b5b6000610699858286016105df565b92505060206106aa858286016105df565b9150509250929050565b6106bd816105b8565b82525050565b60006020820190506106d860008301846106b4565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610718826105b8565b915063ffffffff820361072e5761072d6106de565b5b600182019050919050565b6000610744826105b8565b915061074f836105b8565b9250828201905063ffffffff81111561076b5761076a6106de565b5b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fdfea26469706673582212209ead5577e72317eb390c1db842de5179a6cf2a8df9cc840fb0a80f11d1fcf07064736f6c63430008110033 \ No newline at end of file diff --git a/actors/evm/tests/contracts/StorageFootprint.hex b/actors/evm/tests/contracts/StorageFootprint.hex new file mode 100644 index 000000000..c382c241d --- /dev/null +++ b/actors/evm/tests/contracts/StorageFootprint.hex @@ -0,0 +1 @@ +608060405234801561001057600080fd5b506107d6806100206000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c806380a5fa431161005b57806380a5fa43146100d95780639ab3c8bb14610109578063aa835c4814610125578063ea437a3a1461014157610088565b806302c6564b1461008d57806327e561f6146100a9578063339f8645146100b357806373ef151a146100cf575b600080fd5b6100a760048036038101906100a291906105f4565b610171565b005b6100b16101f0565b005b6100cd60048036038101906100c89190610621565b6102f1565b005b6100d761036d565b005b6100f360048036038101906100ee9190610674565b6103ae565b60405161010091906106c3565b60405180910390f35b610123600480360381019061011e9190610621565b61043b565b005b61013f600480360381019061013a91906105f4565b6104b7565b005b61015b60048036038101906101569190610674565b610536565b60405161016891906106c3565b60405180910390f35b6000600190505b8163ffffffff168163ffffffff16116101ec5760018190806001815401808255809150506001900390600052602060002090600891828204019190066004029091909190916101000a81548163ffffffff021916908363ffffffff16021790555080806101e49061070d565b915050610178565b5050565b60016000808282829054906101000a900463ffffffff166102119190610739565b92506101000a81548163ffffffff021916908363ffffffff1602179055506001600060048282829054906101000a900463ffffffff166102519190610739565b92506101000a81548163ffffffff021916908363ffffffff1602179055506001600060088282829054906101000a900463ffffffff166102919190610739565b92506101000a81548163ffffffff021916908363ffffffff16021790555060016000600c8282829054906101000a900463ffffffff166102d19190610739565b92506101000a81548163ffffffff021916908363ffffffff160217905550565b60008390505b82846103039190610739565b63ffffffff168163ffffffff1610156103675781600460008363ffffffff1663ffffffff16815260200190815260200160002060006101000a81548163ffffffff021916908363ffffffff160217905550808061035f9061070d565b9150506102f7565b50505050565b60016000808282829054906101000a900463ffffffff1661038e9190610739565b92506101000a81548163ffffffff021916908363ffffffff160217905550565b6000808390505b82846103c19190610739565b63ffffffff168163ffffffff1610156104345760018163ffffffff16815481106103ee576103ed610771565b5b90600052602060002090600891828204019190066004029054906101000a900463ffffffff168261041f9190610739565b9150808061042c9061070d565b9150506103b5565b5092915050565b60008390505b828461044d9190610739565b63ffffffff168163ffffffff1610156104b15781600360008363ffffffff1663ffffffff16815260200190815260200160002060006101000a81548163ffffffff021916908363ffffffff16021790555080806104a99061070d565b915050610441565b50505050565b6000600190505b8163ffffffff168163ffffffff16116105325760028190806001815401808255809150506001900390600052602060002090600891828204019190066004029091909190916101000a81548163ffffffff021916908363ffffffff160217905550808061052a9061070d565b9150506104be565b5050565b6000808390505b82846105499190610739565b63ffffffff168163ffffffff1610156105ac57600360008263ffffffff1663ffffffff16815260200190815260200160002060009054906101000a900463ffffffff16826105979190610739565b915080806105a49061070d565b91505061053d565b5092915050565b600080fd5b600063ffffffff82169050919050565b6105d1816105b8565b81146105dc57600080fd5b50565b6000813590506105ee816105c8565b92915050565b60006020828403121561060a576106096105b3565b5b6000610618848285016105df565b91505092915050565b60008060006060848603121561063a576106396105b3565b5b6000610648868287016105df565b9350506020610659868287016105df565b925050604061066a868287016105df565b9150509250925092565b6000806040838503121561068b5761068a6105b3565b5b6000610699858286016105df565b92505060206106aa858286016105df565b9150509250929050565b6106bd816105b8565b82525050565b60006020820190506106d860008301846106b4565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610718826105b8565b915063ffffffff820361072e5761072d6106de565b5b600182019050919050565b6000610744826105b8565b915061074f836105b8565b9250828201905063ffffffff81111561076b5761076a6106de565b5b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fdfea26469706673582212209ead5577e72317eb390c1db842de5179a6cf2a8df9cc840fb0a80f11d1fcf07064736f6c63430008110033 \ No newline at end of file diff --git a/actors/evm/tests/contracts/StorageFootprint.signatures b/actors/evm/tests/contracts/StorageFootprint.signatures new file mode 100644 index 000000000..8a7689921 --- /dev/null +++ b/actors/evm/tests/contracts/StorageFootprint.signatures @@ -0,0 +1,9 @@ +Function signatures: +02c6564b: array1_push(uint32) +80a5fa43: array1_sum(uint32,uint32) +aa835c48: array2_push(uint32) +73ef151a: inc_counter1() +27e561f6: inc_counters() +9ab3c8bb: mapping1_set(uint32,uint32,uint32) +ea437a3a: mapping1_sum(uint32,uint32) +339f8645: mapping2_set(uint32,uint32,uint32) diff --git a/actors/evm/tests/contracts/StorageFootprint.sol b/actors/evm/tests/contracts/StorageFootprint.sol new file mode 100644 index 000000000..d72bf5aeb --- /dev/null +++ b/actors/evm/tests/contracts/StorageFootprint.sol @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract StorageFootprint { + // Create enough counters that they would not fit in the default HAMT bucket size of 3. + uint32 counter1; + uint32 counter2; + uint32 counter3; + uint32 counter4; + + // Create two dynamic arrays to demonstrate that their costs are independent of each other because + // they occupy different parts of the tree, and then that their items are laid out contiguously. + uint32[] array1; + uint32[] array2; + + // Create two mappings to demonstrate that their costs are independent of each other because + // they occupy different parts of the tree, and that their items are also laid out randomly. + mapping(uint32 => uint32) mapping1; + mapping(uint32 => uint32) mapping2; + + // Increment a single counter. + function inc_counter1() public { + counter1 += 1; + } + + // Increment all counters to see if there is a cost difference compared to incrementing a single one. + function inc_counters() public { + counter1 += 1; + counter2 += 1; + counter3 += 1; + counter4 += 1; + } + + // Push `n` more elements to `array1`, to measure how much it costs to extend it with varying + // number of items, depending on how many fit into a node. + function array1_push(uint32 n) public { + // Starting from 1 because pushing 0 doesn't increase the storage size. + for (uint32 i = 1; i <= n; i++) { + array1.push(i); + } + } + + // Push `n` more elements to `array2`, to see if the size of `array1` has any bearing on it. + function array2_push(uint32 n) public { + for (uint32 i = 1; i <= n; i++) { + array2.push(i); + } + } + + // Set `n` consecutive keys starting from `k` to the value `v` in the first mapping. + // Call it with varying number of items to see that for maps batch size doesn't make a difference. + function mapping1_set( + uint32 k, + uint32 n, + uint32 v + ) public { + for (uint32 i = k; i < k + n; i++) { + mapping1[i] = v; + } + } + + // Set `n` consecutive keys starting from `k` to the value `v` in the second mapping. + // Can be used to demonstrate that maps don't influence each others' cost. + function mapping2_set( + uint32 k, + uint32 n, + uint32 v + ) public { + for (uint32 i = k; i < k + n; i++) { + mapping2[i] = v; + } + } + + // Sum of items in a range of `array1`. + // Use this to see how much it costs to retrieve varying number of ranges of items from the array. + function array1_sum(uint32 k, uint32 n) public view returns (uint32 sum) { + for (uint32 i = k; i < k + n; i++) { + sum += array1[i]; + } + return sum; + } + + // Sum the items in a range of `mapping1`. + // Can be used to contrast with the cost of retrieving similar ranges of items from the array. + function mapping1_sum(uint32 k, uint32 n) public view returns (uint32 sum) { + for (uint32 i = k; i < k + n; i++) { + sum += mapping1[i]; + } + return sum; + } +} diff --git a/actors/evm/tests/contracts/StorageFootprint_storage.json b/actors/evm/tests/contracts/StorageFootprint_storage.json new file mode 100644 index 000000000..42203a52a --- /dev/null +++ b/actors/evm/tests/contracts/StorageFootprint_storage.json @@ -0,0 +1 @@ +{"storage":[{"astId":3,"contract":"tests/contracts/StorageFootprint.sol:StorageFootprint","label":"counter1","offset":0,"slot":"0","type":"t_uint32"},{"astId":5,"contract":"tests/contracts/StorageFootprint.sol:StorageFootprint","label":"counter2","offset":4,"slot":"0","type":"t_uint32"},{"astId":7,"contract":"tests/contracts/StorageFootprint.sol:StorageFootprint","label":"counter3","offset":8,"slot":"0","type":"t_uint32"},{"astId":9,"contract":"tests/contracts/StorageFootprint.sol:StorageFootprint","label":"counter4","offset":12,"slot":"0","type":"t_uint32"},{"astId":12,"contract":"tests/contracts/StorageFootprint.sol:StorageFootprint","label":"array1","offset":0,"slot":"1","type":"t_array(t_uint32)dyn_storage"},{"astId":15,"contract":"tests/contracts/StorageFootprint.sol:StorageFootprint","label":"array2","offset":0,"slot":"2","type":"t_array(t_uint32)dyn_storage"},{"astId":19,"contract":"tests/contracts/StorageFootprint.sol:StorageFootprint","label":"mapping1","offset":0,"slot":"3","type":"t_mapping(t_uint32,t_uint32)"},{"astId":23,"contract":"tests/contracts/StorageFootprint.sol:StorageFootprint","label":"mapping2","offset":0,"slot":"4","type":"t_mapping(t_uint32,t_uint32)"}],"types":{"t_array(t_uint32)dyn_storage":{"base":"t_uint32","encoding":"dynamic_array","label":"uint32[]","numberOfBytes":"32"},"t_mapping(t_uint32,t_uint32)":{"encoding":"mapping","key":"t_uint32","label":"mapping(uint32 => uint32)","numberOfBytes":"32","value":"t_uint32"},"t_uint32":{"encoding":"inplace","label":"uint32","numberOfBytes":"4"}}} \ No newline at end of file diff --git a/actors/evm/tests/contracts/callvariants.eas b/actors/evm/tests/contracts/callvariants.eas new file mode 100644 index 000000000..f2f3321c5 --- /dev/null +++ b/actors/evm/tests/contracts/callvariants.eas @@ -0,0 +1,19 @@ +# this is a multi-purpose contract for testing various scenarios of STATICCALL and DELEGATECALL + +# initialization: store the id address as a nonce at slot 0 +address +push1 0x00 +sstore + +# contract code +%push(body_end - body_begin) +dup1 +%push(body_begin) +push1 0x00 +codecopy +push1 0x00 +return + +body_begin: +%include("callvariants_body.eas") +body_end: \ No newline at end of file diff --git a/actors/evm/tests/contracts/callvariants.hex b/actors/evm/tests/contracts/callvariants.hex new file mode 100644 index 000000000..9eef5886e --- /dev/null +++ b/actors/evm/tests/contracts/callvariants.hex @@ -0,0 +1 @@ +306000556102408060106000396000f360003560e01c8060011460865780600214610208578060031460a65780600414610214578060051460c6578060061460ec578060071461010e578060081461013457806009146101565780600a146101965780600b146101bc5780600c146101565780600d146101e25780600e146101965780600f14610176578060101461023657600080fd5b60206000600260e01b600052600460006004355afa156102285760206000f35b60206000600460e01b600052600460006004355afa156102285760206000f35b60206000600660e01b600052602435600452602460006004355afa156102285760206000f35b60206000600260e01b6000526004600060006004355af1156102285760206000f35b60206000600860e01b600052602435600452602460006004355afa156102285760206000f35b60206000600460e01b6000526004600060006004355af1156102285760206000f35b60206000600260e01b600052600460006004355af4156102285760206000f35b60206000601060e01b600052600460006004355af4156102285760206000f35b60206000600460e01b600052600460006004355af4156102285760005460005260206000f35b60206000600c60e01b600052602435600452602460006004355afa156102285760206000f35b60206000600e60e01b600052602435600452602460006004355afa156102285760206000f35b60005460005260206000f35b63ffffff4260005560005460005260206000f35b63deadbeef6000526004601cfd5b3460005260206000f3 \ No newline at end of file diff --git a/actors/evm/tests/contracts/callvariants_body.eas b/actors/evm/tests/contracts/callvariants_body.eas new file mode 100644 index 000000000..2d03ed3e0 --- /dev/null +++ b/actors/evm/tests/contracts/callvariants_body.eas @@ -0,0 +1,375 @@ +# this is the body of the callvariants contract + +# dispatch macros +%macro dispatch_begin() + push1 0x00 + calldataload + push1 0xe0 # 28 byte shift == 224 bits + shr +%end + +%macro dispatch(method, lbl) + dup1 + %push($method) + eq + %push($lbl) + jumpi +%end + +%macro dispatch_end() + push1 0x00 + dup1 + revert +%end + +%macro check() + iszero + %push(call_failed) + jumpi +%end + +# method dispatch +%dispatch_begin() + +### STATICCALL +# A -> staticcall -> B (read) OK +%dispatch(1, do_staticcall2) +%dispatch(2, do_read) + +# A -> staticcall -> B (write) FAIL +%dispatch(3, do_staticcall4) +%dispatch(4, do_write) + +# A -> staticcall -> B -> call -> C (read) OK +%dispatch(5, do_staticcall6) +%dispatch(6, do_call2) + +# A -> staticcall -> B -> call -> C (write) FAIL +%dispatch(7, do_staticcall8) +%dispatch(8, do_call4) + +### DELEGATECALL +# A -> delegatecall -> B (read) OK +%dispatch(9, do_delegatecall2) + +# A -> delegatecall -> B (write) -> return (read) OK +%dispatch(10, do_delegatecall4) + +# A -> staticcall -> B -> delegatecall -> C (read) OK +%dispatch(11, do_staticcall12) +%dispatch(12, do_delegatecall2) + +# A -> staticcall -> B -> delegatecall -> C (write) FAIL +%dispatch(13, do_staticcall14) +%dispatch(14, do_delegatecall4) + +# A -> delegatecall -> B (check value) +%dispatch(15, do_delegatecall3) +%dispatch(16, check_value_received) + +%dispatch_end() + +#### CALLERS + +# do_staticcall2(address) +do_staticcall2: +jumpdest +push1 0x20 # staticcall ret length +push1 0x00 # staticcall ret offset +push1 0x02 # arg: method +push1 0xe0 # shift 224 bits (28 bytes) +shl +push1 0x00 # arg: offset +mstore # args +push1 0x04 # staticcall args length +push1 0x00 # staticcall args offset +push1 0x04 # input (dest) offset +calldataload # staticcall dest +gas +staticcall # do it! +%check() +push1 0x20 # ret length +push1 0x00 # ret offset +return + +# do_staticcall4(address) +do_staticcall4: +jumpdest +push1 0x20 # staticcall ret length +push1 0x00 # staticcall ret offset +push1 0x04 # arg: method +push1 0xe0 # shift 224 bits (28 bytes) +shl +push1 0x00 # arg: offset +mstore # args +push1 0x04 # staticcall args length +push1 0x00 # staticcall args offset +push1 0x04 # input (dest) offset +calldataload # staticcall dest +gas +staticcall # do it! +%check() +push1 0x20 # ret length +push1 0x00 # ret offset +return + +# do_staticcall6(address, address) +do_staticcall6: +jumpdest +push1 0x20 # staticcall ret length +push1 0x00 # staticcall ret offset +push1 0x06 # arg1: method +push1 0xe0 # shift 224 bits (28 bytes) +shl +push1 0x00 # arg1: offset +mstore # arg1 +push1 0x24 # arg2: address2 offset +calldataload # address2 +push1 0x04 # arg2: offset +mstore # arg2 +push1 0x24 # staticcall args length +push1 0x00 # staticcall args offset +push1 0x04 # input (dest) offset +calldataload # staticcall dest +gas +staticcall # do it! +%check() +push1 0x20 # ret length +push1 0x00 # ret offset +return + +# do_call2(address) +do_call2: +jumpdest +push1 0x20 # call ret length +push1 0x00 # call ret offset +push1 0x02 # arg: method +push1 0xe0 # shift 224 bits (28 bytes) +shl +push1 0x00 # arg: offset +mstore # args +push1 0x04 # call args length +push1 0x00 # call args offset +push1 0x00 # value +push1 0x04 # input (dest) offset +calldataload # call dest +gas +call # do it! +%check() +push1 0x20 # ret length +push1 0x00 # ret offset +return + +# do_staticcall8(address, address) +do_staticcall8: +jumpdest +push1 0x20 # staticcall ret length +push1 0x00 # staticcall ret offset +push1 0x08 # arg1: method +push1 0xe0 # shift 224 bits (28 bytes) +shl +push1 0x00 # arg1: offset +mstore # arg1 +push1 0x24 # arg2: ddress2 offset +calldataload # address2 +push1 0x04 # arg2: offset +mstore # arg2 +push1 0x24 # staticcall args length +push1 0x00 # staticcall args offset +push1 0x04 # input (dest) offset +calldataload # staticcall dest +gas +staticcall # do it! +%check() +push1 0x20 # ret length +push1 0x00 # ret offset +return + +# do_call4(address) +do_call4: +jumpdest +push1 0x20 # ccall ret length +push1 0x00 # call ret offset +push1 0x04 # arg: method +push1 0xe0 # shift 224 bits (28 bytes) +shl +push1 0x00 # arg: offset +mstore # args +push1 0x04 # call args length +push1 0x00 # call args offset +push1 0x00 # value +push1 0x04 # input (dest) offset +calldataload # icall dest +gas +call # do it! +%check() +push1 0x20 # ret length +push1 0x00 # ret offset +return + +# do_delegatecall2(address) +do_delegatecall2: +jumpdest + +push1 0x20 # delegate ret length +push1 0x00 # delegate ret offset +push1 0x02 # arg: method +push1 0xe0 # shift 224 bits (28 bytes) +shl +push1 0x00 # arg: offset +mstore # args +push1 0x04 # delegate args length +push1 0x00 # delegate args offset +push1 0x04 # input (dest) offset +calldataload # delegate dest +gas +delegatecall # do it! +%check() +push1 0x20 # ret length +push1 0x00 # ret offset +return + +# do_delegatecall3(address) +do_delegatecall3: +jumpdest + +push1 0x20 # delegate ret length +push1 0x00 # delegate ret offset +push1 0x10 # arg: method +push1 0xe0 # shift 224 bits (28 bytes) +shl +push1 0x00 # arg: offset +mstore # args +push1 0x04 # delegate args length +push1 0x00 # delegate args offset +push1 0x04 # input (dest) offset +calldataload # delegate dest +gas +delegatecall # do it! +%check() +push1 0x20 # ret length +push1 0x00 # ret offset +return + +# do_delegatecall4(address) +do_delegatecall4: +jumpdest +push1 0x20 # delegate ret length +push1 0x00 # delegate ret offset +push1 0x04 # arg: method +push1 0xe0 # shift 224 bits (28 bytes) +shl +push1 0x00 # arg: offset +mstore # args +push1 0x04 # delegate args length +push1 0x00 # delegate args offset +push1 0x04 # input (dest) offset +calldataload # delegate dest +gas +delegatecall # do it! +%check() +push1 0x00 # slot +sload # read +push1 0x00 # ret offset +mstore # ret data +push1 0x20 # ret length +push1 0x00 # ret offset +return + +# do_staticcall12(address, address) +do_staticcall12: +jumpdest +push1 0x20 # staticcall ret length +push1 0x00 # staticcall ret offset +push1 0x0c # arg1: method +push1 0xe0 # shift 224 bits (28 bytes) +shl +push1 0x00 # arg1: offset +mstore # arg1 +push1 0x24 # arg2: ddress2 offset +calldataload # address2 +push1 0x04 # arg2: offset +mstore # arg2 +push1 0x24 # staticcall args length +push1 0x00 # staticcall args offset +push1 0x04 # input (dest) offset +calldataload # staticcall dest +gas +staticcall # do it! +%check() +push1 0x20 # ret length +push1 0x00 # ret offset +return + +# do_staticcall14(address, address) +do_staticcall14: +jumpdest +push1 0x20 # staticcall ret length +push1 0x00 # staticcall ret offset +push1 0x0e # arg1: method +push1 0xe0 # shift 224 bits (28 bytes) +shl +push1 0x00 # arg1: offset +mstore # arg1 +push1 0x24 # arg2: ddress2 offset +calldataload # address2 +push1 0x04 # arg2: offset +mstore # arg2 +push1 0x24 # staticcall args length +push1 0x00 # staticcall args offset +push1 0x04 # input (dest) offset +calldataload # staticcall dest +gas +staticcall # do it! +%check() +push1 0x20 # ret length +push1 0x00 # ret offset +return + + +#### TERMINALS + +# do_read +do_read: +jumpdest +push1 0x00 +sload +push1 0x00 +mstore +push1 0x20 +push1 0x00 +return + +# do_write +do_write: +jumpdest +%push(0xffffff42) +push1 0x00 +sstore +push1 0x00 +sload +push1 0x00 +mstore +push1 0x20 +push1 0x00 +return + +# call_failed +call_failed: +jumpdest +%push(0xdeadbeef) +push1 0x00 +mstore +push1 0x04 +push1 0x1c +revert + +# return the value received +check_value_received: +jumpdest +callvalue +push1 0x00 +mstore +push1 0x20 +push1 0x00 +return diff --git a/actors/evm/tests/contracts/output/FeSimplecoin/FeSimplecoin.bin b/actors/evm/tests/contracts/output/FeSimplecoin/FeSimplecoin.bin new file mode 100644 index 000000000..b459879de --- /dev/null +++ b/actors/evm/tests/contracts/output/FeSimplecoin/FeSimplecoin.bin @@ -0,0 +1 @@ +6103da803803906040518015610043575b828101604052393260005260006020526127106008600160fb1b03604060002060051c165561038e8061004c6000396000f35b50606061001056fe60003560e01c806390b98a11146100675780637bd703e8146100525763f8b2cb4f1461002757005b61003c61003736600319016102d7565b610282565b61004f61004761029a565b918252602090565b90f35b61003c61006236600319016102d7565b610230565b60431936016100ad575b6100896004358060a01c6100a0575b602435906100ba565b61004f61009461029a565b60ff9092168252602090565b6100a86102ac565b610080565b6100b56102ac565b610071565b6000929160ff816100d9336000526000602052604060002060ff191690565b8060051c549060f884199160031b1661010003011c1016806001146102275715610101575050565b6102089293508061018d6102039261014361012a336000526000602052604060002060ff191690565b8060051c549060f860ff199160031b1661010003011c90565b81811061021a575b03610164336000526000602052604060002060ff191690565b9060ff1960f88360031b16610100030191600019831b91829160051c931b169019825416179055565b6101ce816101ac61012a866000526000602052604060002060ff191690565b8119811161020d575b01610164856000526000602052604060002060ff191690565b6101d661032f565b9233849060409260018060601b0391828451169060601b17835260208301918251169060601b1790520152565b61034c565b600190565b610215610305565b6101b5565b610222610305565b61014b565b50600093505050565b610248906000526000602052604060002060ff191690565b8060051c549060f860ff199160031b1661010003011c806000190460021181151516610275575b60011b90565b61027d610305565b61026f565b61012a906000526000602052604060002060ff191690565b6040519081156102a657565b60609150565b5060246102b761029a565b80516001600160e01b0316632e36a70960e01b1781526101036004820152fd5b6020036102f8575b6004358060a01c6102ed5790565b6102f56102ac565b90565b6103006102ac565b6102df565b50602461031061029a565b80516001600160e01b0316634e487b7160e01b17815260116004820152fd5b604051908115610343575b60608201604052565b6060915061033a565b61035461029a565b604082015181527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60208084015160601c935160601c92a356 \ No newline at end of file diff --git a/actors/evm/tests/contracts/output/FeSimplecoin/FeSimplecoin_abi.json b/actors/evm/tests/contracts/output/FeSimplecoin/FeSimplecoin_abi.json new file mode 100644 index 000000000..40bc61693 --- /dev/null +++ b/actors/evm/tests/contracts/output/FeSimplecoin/FeSimplecoin_abi.json @@ -0,0 +1,92 @@ +[ + { + "type": "constructor", + "name": "__init__", + "inputs": [], + "outputs": [], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "sendCoin", + "inputs": [ + { + "name": "receiver", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "getBalanceInEth", + "inputs": [ + { + "name": "addr", + "type": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getBalance", + "inputs": [ + { + "name": "addr", + "type": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "event", + "name": "Transfer", + "inputs": [ + { + "name": "from", + "type": "address", + "indexed": true + }, + { + "name": "to", + "type": "address", + "indexed": true + }, + { + "name": "value", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Context", + "inputs": [], + "anonymous": false + } +] \ No newline at end of file diff --git a/actors/evm/tests/contracts/selfdestruct.hex b/actors/evm/tests/contracts/selfdestruct.hex new file mode 100644 index 000000000..fd4eaf0c4 --- /dev/null +++ b/actors/evm/tests/contracts/selfdestruct.hex @@ -0,0 +1 @@ +608060405234801561001057600080fd5b5060f58061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c806335f46994146037578063901717d114603f575b600080fd5b603d6059565b005b60456086565b6040516050919060a6565b60405180910390f35b73ff000000000000000000000000000000000003e973ffffffffffffffffffffffffffffffffffffffff16ff5b60006001905090565b6000819050919050565b60a081608f565b82525050565b600060208201905060b960008301846099565b9291505056fea26469706673582212205e642139367a73faa34d4286b3d045c006891ee95bed3ffc39e14604bc9c3a7d64736f6c63430008110033 \ No newline at end of file diff --git a/actors/evm/tests/contracts/selfdestruct.sol b/actors/evm/tests/contracts/selfdestruct.sol new file mode 100644 index 000000000..7256128f3 --- /dev/null +++ b/actors/evm/tests/contracts/selfdestruct.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract Selfdestruct { + function die() public { + selfdestruct( + payable(address(0xFF000000000000000000000000000000000003E9)) + ); + } + function one() pure public returns (uint256){ + return 0x1; + } +} diff --git a/actors/evm/tests/contracts/simplecoin.hex b/actors/evm/tests/contracts/simplecoin.hex new file mode 100644 index 000000000..55b83cb12 --- /dev/null +++ b/actors/evm/tests/contracts/simplecoin.hex @@ -0,0 +1 @@ +608060405234801561001057600080fd5b506127106000803273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555061051c806100656000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80637bd703e81461004657806390b98a1114610076578063f8b2cb4f146100a6575b600080fd5b610060600480360381019061005b919061030a565b6100d6565b60405161006d9190610350565b60405180910390f35b610090600480360381019061008b9190610397565b6100f4565b60405161009d91906103f2565b60405180910390f35b6100c060048036038101906100bb919061030a565b61025f565b6040516100cd9190610350565b60405180910390f35b600060026100e38361025f565b6100ed919061043c565b9050919050565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410156101455760009050610259565b816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254610193919061047e565b92505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546101e891906104b2565b925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161024c9190610350565b60405180910390a3600190505b92915050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006102d7826102ac565b9050919050565b6102e7816102cc565b81146102f257600080fd5b50565b600081359050610304816102de565b92915050565b6000602082840312156103205761031f6102a7565b5b600061032e848285016102f5565b91505092915050565b6000819050919050565b61034a81610337565b82525050565b60006020820190506103656000830184610341565b92915050565b61037481610337565b811461037f57600080fd5b50565b6000813590506103918161036b565b92915050565b600080604083850312156103ae576103ad6102a7565b5b60006103bc858286016102f5565b92505060206103cd85828601610382565b9150509250929050565b60008115159050919050565b6103ec816103d7565b82525050565b600060208201905061040760008301846103e3565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061044782610337565b915061045283610337565b925082820261046081610337565b915082820484148315176104775761047661040d565b5b5092915050565b600061048982610337565b915061049483610337565b92508282039050818111156104ac576104ab61040d565b5b92915050565b60006104bd82610337565b91506104c883610337565b92508282019050808211156104e0576104df61040d565b5b9291505056fea26469706673582212205ede41ff9072784ccc19ac18de0781558d305a8139361fa85dc51a8614e47d8c64736f6c63430008110033 \ No newline at end of file diff --git a/actors/evm/tests/contracts/simplecoin.sol b/actors/evm/tests/contracts/simplecoin.sol new file mode 100644 index 000000000..5318b0cb8 --- /dev/null +++ b/actors/evm/tests/contracts/simplecoin.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.4.2; + +contract SimpleCoin { + mapping(address => uint256) balances; + + event Transfer(address indexed _from, address indexed _to, uint256 _value); + + constructor() { + balances[tx.origin] = 10000; + } + + function sendCoin(address receiver, uint256 amount) + public + returns (bool sufficient) + { + if (balances[msg.sender] < amount) return false; + balances[msg.sender] -= amount; + balances[receiver] += amount; + emit Transfer(msg.sender, receiver, amount); + return true; + } + + function getBalanceInEth(address addr) public view returns (uint256) { + return getBalance(addr) * 2; + } + + function getBalance(address addr) public view returns (uint256) { + return balances[addr]; + } +} diff --git a/actors/evm/tests/create.rs b/actors/evm/tests/create.rs new file mode 100644 index 000000000..35b92f50e --- /dev/null +++ b/actors/evm/tests/create.rs @@ -0,0 +1,173 @@ +mod asm; + +use evm::ext::eam; +use fil_actor_evm as evm; +use fil_actors_evm_shared::address::EthAddress; +use fil_actors_runtime::EAM_ACTOR_ADDR; +use fvm_ipld_encoding::ipld_block::IpldBlock; +use fvm_shared::econ::TokenAmount; +use fvm_shared::error::ExitCode; +use fvm_shared::sys::SendFlags; + +mod util; + +#[allow(dead_code)] +pub fn magic_precompile_contract() -> Vec { + let init = ""; + let body = r#" +# magic value, used as initcode +push16 0x666F6F206261722062617A20626F7879 # foo bar baz boxy +push2 0x0100 # offset of input data +mstore # "bytecode" + + +%dispatch_begin() +%dispatch(0x00, test_create) +%dispatch(0x01, test_create2) +%dispatch_end() + +test_create: + jumpdest + push1 0x10 # in size (16 bytes) + push2 0x0110 # in offset + push1 0x01 # value (attoFil) + create + %return_stack_word() + +test_create2: + jumpdest + # salt + push9 0x796573206D616E6921 # yes mani! + push1 0x10 # in size (16 bytes) + push2 0x0110 # in offset + push1 0x01 # endowment (attoFil) + create2 + %return_stack_word() +"#; + + asm::new_contract("magic-precompile", init, body).unwrap() +} + +#[test] +fn test_create() { + const GAS_AVAILABLE: u64 = 64_000_000; + const GAS_SUBCALL: u64 = 63_000_000; + + let contract = magic_precompile_contract(); + let mut rt = util::construct_and_verify(contract); + + let fake_eth_addr = EthAddress(hex_literal::hex!("CAFEB0BA00000000000000000000000000000000")); + let fake_ret = eam::CreateReturn { + actor_id: 12345, + eth_address: fake_eth_addr, + robust_address: Some((&fake_eth_addr).try_into().unwrap()), + }; + + let salt = + hex_literal::hex!("0000000000000000000000000000000000000000000000796573206D616E6921"); + + let create2_params = eam::Create2Params { + code: hex_literal::hex!("666F6F206261722062617A20626F7879").to_vec(), + salt, + }; + + let mut create_params = eam::CreateParams { + code: hex_literal::hex!("666F6F206261722062617A20626F7879").to_vec(), + nonce: 1, + }; + + // byte 3 is method num + let mut contract_params = [0u8; 32]; + + // invoke contract -- create + { + rt.add_balance(TokenAmount::from_atto(1)); + + rt.expect_gas_available(GAS_AVAILABLE); + rt.expect_send( + EAM_ACTOR_ADDR, + eam::CREATE_METHOD_NUM, + IpldBlock::serialize_cbor(&create_params).unwrap(), + TokenAmount::from_atto(1), + Some(GAS_SUBCALL), + SendFlags::empty(), + IpldBlock::serialize_cbor(&fake_ret).unwrap(), + ExitCode::OK, + None, + ); + + let result = util::invoke_contract(&mut rt, &contract_params); + let result: [u8; 20] = result[12..].try_into().unwrap(); + let result = EthAddress(result); + // make sure we arent doing weird things to EAM's return value + assert_eq!(result, fake_eth_addr); + } + + // invoke contract -- create with new nonce + { + create_params.nonce += 1; + + rt.add_balance(TokenAmount::from_atto(1)); + + rt.expect_gas_available(GAS_AVAILABLE); + rt.expect_send( + EAM_ACTOR_ADDR, + eam::CREATE_METHOD_NUM, + IpldBlock::serialize_cbor(&create_params).unwrap(), + TokenAmount::from_atto(1), + Some(GAS_SUBCALL), + SendFlags::empty(), + IpldBlock::serialize_cbor(&fake_ret).unwrap(), + ExitCode::OK, + None, + ); + + let result = util::invoke_contract(&mut rt, &contract_params); + let result: [u8; 20] = result[12..].try_into().unwrap(); + let result = EthAddress(result); + // make sure we arent doing weird things to EAM's return value + assert_eq!(result, fake_eth_addr); + } + + contract_params[3] = 0x01; + + // invoke contract -- create2 + { + rt.add_balance(TokenAmount::from_atto(1)); + + rt.expect_gas_available(GAS_AVAILABLE); + rt.expect_send( + EAM_ACTOR_ADDR, + eam::CREATE2_METHOD_NUM, + IpldBlock::serialize_cbor(&create2_params).unwrap(), + TokenAmount::from_atto(1), + Some(GAS_SUBCALL), + SendFlags::empty(), + IpldBlock::serialize_cbor(&fake_ret).unwrap(), + ExitCode::OK, + None, + ); + + let result = util::invoke_contract(&mut rt, &contract_params); + let result: [u8; 20] = result[12..].try_into().unwrap(); + let result = EthAddress(result); + // make sure we arent doing weird things to EAM's return value + assert_eq!(result, fake_eth_addr); + } + + // not enough funds -- create2 + { + let result = util::invoke_contract(&mut rt, &contract_params); + assert_eq!(&result[..], &[0; 32]); + } + + contract_params[3] = 0x00; + + // not enough funds -- create + { + create_params.nonce += 3; + + let result = util::invoke_contract(&mut rt, &contract_params); + assert_eq!(&result[..], &[0; 32]); + } +} diff --git a/actors/evm/tests/delegate_call.rs b/actors/evm/tests/delegate_call.rs new file mode 100644 index 000000000..60ba5f6bb --- /dev/null +++ b/actors/evm/tests/delegate_call.rs @@ -0,0 +1,136 @@ +use fil_actor_evm::{DelegateCallParams, Method}; +use fil_actors_evm_shared::{address::EthAddress, uints::U256}; +use fil_actors_runtime::{runtime::EMPTY_ARR_CID, test_utils::EVM_ACTOR_CODE_ID}; +use fvm_ipld_encoding::{ipld_block::IpldBlock, BytesSer, RawBytes, DAG_CBOR}; +use fvm_shared::{ + address::Address as FILAddress, econ::TokenAmount, error::ExitCode, sys::SendFlags, +}; +use num_traits::Zero; + +mod asm; +mod util; + +#[allow(dead_code)] +pub fn delegatecall_proxy_contract() -> Vec { + let init = ""; + let body = r#" +# this contract takes an address and the call payload and proxies a call to that address +# get call payload size +push1 0x20 +calldatasize +sub +# store payload to mem 0x00 +push1 0x20 +push1 0x00 +calldatacopy + +# prepare the proxy call +# output offset and size -- 0 in this case, we use returndata +push2 0x00 +push1 0x00 +# input offset and size +push1 0x20 +calldatasize +sub +push1 0x05 +# value +push1 0x00 +# dest address +push1 0x00 +calldataload +# gas +push4 0xffffffff +# do the call +delegatecall + +# return result through +returndatasize +push1 0x00 +push1 0x00 +returndatacopy +returndatasize +push1 0x00 +return +"#; + + asm::new_contract("delegatecall-proxy", init, body).unwrap() +} + +#[test] +fn test_delegate_call_caller() { + let contract = delegatecall_proxy_contract(); + + // construct the proxy + let mut rt = util::construct_and_verify(contract); + + // create a mock target and proxy a call through the proxy + let target_id = 0x100; + let target = FILAddress::new_id(target_id); + let evm_target = EthAddress(hex_literal::hex!("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef")); + let f4_target: FILAddress = evm_target.try_into().unwrap(); + rt.actor_code_cids.insert(target, *EVM_ACTOR_CODE_ID); + rt.set_delegated_address(target.id().unwrap(), f4_target); + rt.receiver = target; + + // set caller that is expected to persist through to subcall + let caller = FILAddress::new_id(0x111); + let evm_caller = EthAddress(util::CONTRACT_ADDRESS); + let f4_caller = evm_caller.try_into().unwrap(); + rt.set_delegated_address(caller.id().unwrap(), f4_caller); + rt.caller = caller; + + let evm_target_word = evm_target.as_evm_word(); + + // dest + method 0 + single byte of data + let mut contract_params = vec![0u8; 37]; + evm_target_word.to_big_endian(&mut contract_params[..32]); + contract_params[36] = 0x01; + + // dest 0 in this test has code cid EMPTY_ARR_CID + + let proxy_call_contract_params = DelegateCallParams { + code: EMPTY_ARR_CID, + input: vec![0, 0, 0, 0, 0x01], + caller: evm_caller, + value: TokenAmount::from_whole(123), + }; + let proxy_call_input_data = Some(IpldBlock { + codec: DAG_CBOR, + data: RawBytes::serialize(proxy_call_contract_params) + .expect("failed to serialize delegate call params") + .to_vec(), + }); + + // expected return data + let return_data = U256::from(0x42); + + rt.set_value(TokenAmount::from_whole(123)); + rt.expect_gas_available(10_000_000_000u64); + rt.expect_send( + target, + Method::GetBytecode as u64, + None, + TokenAmount::zero(), + None, + SendFlags::READ_ONLY, + IpldBlock::serialize_cbor(&EMPTY_ARR_CID).expect("failed to serialize bytecode hash"), + ExitCode::OK, + None, + ); + + rt.expect_send( + target, + Method::InvokeContractDelegate as u64, + proxy_call_input_data, + TokenAmount::zero(), + Some(0xffffffff), + SendFlags::empty(), + IpldBlock::serialize_cbor(&BytesSer(&return_data.to_bytes())) + .expect("failed to serialize return data"), + ExitCode::OK, + None, + ); + + let result = util::invoke_contract(&mut rt, &contract_params); + assert_eq!(U256::from_big_endian(&result), return_data); +} diff --git a/actors/evm/tests/env.rs b/actors/evm/tests/env.rs new file mode 100644 index 000000000..529bb2f35 --- /dev/null +++ b/actors/evm/tests/env.rs @@ -0,0 +1,97 @@ +use ethers::{ + abi::Detokenize, + prelude::{builders::ContractCall, decode_function_data}, + providers::{MockProvider, Provider}, +}; +use fil_actor_evm as evm; +use fil_actors_evm_shared::address::EthAddress; +use fil_actors_runtime::{ + test_utils::{MockRuntime, EVM_ACTOR_CODE_ID, INIT_ACTOR_CODE_ID}, + INIT_ACTOR_ADDR, +}; +use fvm_ipld_blockstore::tracking::{BSStats, TrackingBlockstore}; +use fvm_ipld_blockstore::MemoryBlockstore; +use fvm_ipld_encoding::ipld_block::IpldBlock; +use fvm_ipld_encoding::{BytesDe, BytesSer}; +use fvm_shared::address::Address; + +/// Alias for a call we will never send to the blockchain. +pub type TestContractCall = ContractCall, R>; + +pub struct TestEnv { + evm_address: Address, + pub runtime: MockRuntime>, +} + +impl TestEnv { + pub fn take_store_stats(&mut self) -> BSStats { + self.runtime.store.stats.take() + } + + pub fn clear_store_stats(&mut self) { + self.take_store_stats(); + } + + /// Create a new test environment where the EVM actor code is already + /// loaded under an actor address. + pub fn new(evm_address: Address) -> Self { + let mut runtime = MockRuntime::new(TrackingBlockstore::new(MemoryBlockstore::new())); + + runtime.actor_code_cids.insert(evm_address, *EVM_ACTOR_CODE_ID); + + Self { evm_address, runtime } + } + + /// Deploy a contract into the EVM actor. + pub fn deploy(&mut self, contract_hex: &str) { + let params = evm::ConstructorParams { + creator: EthAddress::from_id(fil_actors_runtime::EAM_ACTOR_ADDR.id().unwrap()), + initcode: hex::decode(contract_hex).unwrap().into(), + }; + // invoke constructor + self.runtime.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); + self.runtime.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); + + self.runtime.set_origin(self.evm_address); + // first actor created is 0 + self.runtime.set_delegated_address( + 0, + Address::new_delegated( + 10, + &hex_literal::hex!("FEEDFACECAFEBEEF000000000000000000000000"), + ) + .unwrap(), + ); + + assert!(self + .runtime + .call::( + evm::Method::Constructor as u64, + IpldBlock::serialize_cbor(¶ms).unwrap(), + ) + .unwrap() + .is_none()); + + self.runtime.verify(); + } + + /// Take a function that calls an ABI method to return a `ContractCall`. + /// Then, instead of calling the contract on-chain, run it through our + /// EVM interpreter in the test runtime. Finally parse the results. + pub fn call(&mut self, call: TestContractCall) -> R { + let input = call.calldata().expect("Should have calldata."); + let input = + IpldBlock::serialize_cbor(&BytesSer(&input)).expect("failed to serialize input data"); + self.runtime.expect_validate_caller_any(); + + let BytesDe(result) = self + .runtime + .call::(evm::Method::InvokeContract as u64, input) + .unwrap() + .unwrap() + .deserialize() + .unwrap(); + + decode_function_data(&call.function, result, false).unwrap() + } +} diff --git a/actors/evm/tests/events.rs b/actors/evm/tests/events.rs new file mode 100644 index 000000000..c8cbe86da --- /dev/null +++ b/actors/evm/tests/events.rs @@ -0,0 +1,132 @@ +mod asm; + +use fil_actors_evm_shared::uints::U256; +use fvm_ipld_encoding::IPLD_RAW; +use fvm_shared::event::{ActorEvent, Entry, Flags}; + +mod util; + +#[allow(dead_code)] +pub fn events_contract() -> Vec { + let init = r#" +"#; + let body = r#" +# method dispatch: +# - 0x00000000 -> log_zero_data +# - 0x00000001 -> log_zero_nodata +# - 0x00000002 -> log_four_data + +%dispatch_begin() +%dispatch(0x00, log_zero_data) +%dispatch(0x01, log_zero_nodata) +%dispatch(0x02, log_four_data) +%dispatch_end() + +#### log a zero topic event with data +log_zero_data: +jumpdest +push8 0x1122334455667788 +push1 0x00 +mstore +push1 0x08 +push1 0x18 ## index 24 into memory as mstore writes a full word +log0 +push1 0x00 +push1 0x00 +return + +#### log a zero topic event with no data +log_zero_nodata: +jumpdest +push1 0x00 +push1 0x00 +log0 +push1 0x00 +push1 0x00 +return + +#### log a four topic event with data +log_four_data: +jumpdest +push8 0x1122334455667788 +push1 0x00 +mstore +push4 0x4444 +push3 0x3333 +push2 0x2222 +push2 0x1111 +push1 0x08 +push1 0x18 ## index 24 into memory as mstore writes a full word +log4 +push1 0x00 +push1 0x00 +return + +"#; + + asm::new_contract("events", init, body).unwrap() +} + +#[test] +fn test_events() { + let contract = events_contract(); + + let mut rt = util::construct_and_verify(contract); + + // log zero with data + let mut contract_params = vec![0u8; 32]; + rt.expect_emitted_event(ActorEvent { + entries: vec![Entry { + flags: Flags::FLAG_INDEXED_ALL, + key: "d".to_string(), + codec: IPLD_RAW, + value: vec![0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88], + }], + }); + util::invoke_contract(&mut rt, &contract_params); + + // log zero without data + contract_params[3] = 0x01; + rt.expect_emitted_event(ActorEvent { entries: vec![] }); + util::invoke_contract(&mut rt, &contract_params); + + // log four with data + contract_params[3] = 0x02; + rt.expect_emitted_event(ActorEvent { + entries: vec![ + Entry { + flags: Flags::FLAG_INDEXED_ALL, + key: "t1".to_string(), + codec: IPLD_RAW, + value: U256::from(0x1111).to_bytes().into(), + }, + Entry { + flags: Flags::FLAG_INDEXED_ALL, + key: "t2".to_string(), + codec: IPLD_RAW, + value: U256::from(0x2222).to_bytes().into(), + }, + Entry { + flags: Flags::FLAG_INDEXED_ALL, + key: "t3".to_string(), + codec: IPLD_RAW, + value: U256::from(0x3333).to_bytes().into(), + }, + Entry { + flags: Flags::FLAG_INDEXED_ALL, + key: "t4".to_string(), + codec: IPLD_RAW, + value: U256::from(0x4444).to_bytes().into(), + }, + Entry { + flags: Flags::FLAG_INDEXED_ALL, + key: "d".to_string(), + codec: IPLD_RAW, + value: vec![0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88], + }, + ], + }); + util::invoke_contract(&mut rt, &contract_params); + + rt.verify(); +} diff --git a/actors/evm/tests/ext_opcodes.rs b/actors/evm/tests/ext_opcodes.rs new file mode 100644 index 000000000..a3eb97ba5 --- /dev/null +++ b/actors/evm/tests/ext_opcodes.rs @@ -0,0 +1,509 @@ +mod asm; + +use cid::Cid; +use evm::BytecodeHash; +use fil_actor_evm as evm; +use fil_actors_evm_shared::uints::U256; +use fil_actors_runtime::runtime::{Primitives, Runtime, EMPTY_ARR_CID}; +use fil_actors_runtime::test_utils::*; +use fvm_ipld_blockstore::Blockstore; +use fvm_ipld_encoding::ipld_block::IpldBlock; +use fvm_shared::address::Address as FILAddress; +use fvm_shared::bigint::Zero; +use fvm_shared::crypto::hash::SupportedHashes; +use fvm_shared::econ::TokenAmount; +use fvm_shared::error::ExitCode; + +mod util; + +use fvm_shared::sys::SendFlags; +use util::{CONTRACT_ID, DUMMY_ACTOR_CODE_ID}; + +#[test] +fn test_extcodesize() { + let bytecode = { + let init = ""; + let body = r#" +%dispatch_begin() +%dispatch(0x00, evm_size) +%dispatch(0x01, native_size) +# TODO update after real account abstraction lands +%dispatch(0x02, evm_account) +%dispatch(0x03, native_account) +%dispatch(0x04, placeholder) +%dispatch(0x05, non_existent) +%dispatch_end() + +evm_size: + jumpdest + # get code size of address f088 + push20 0xff00000000000000000000000000000000000088 + extcodesize + %return_stack_word() + +native_size: + jumpdest + # native actor ID + push20 0xff00000000000000000000000000000000000089 + extcodesize + %return_stack_word() + +evm_account: + jumpdest + # evm account + push20 0xff00000000000000000000000000000000000101 + extcodesize + %return_stack_word() + +native_account: + jumpdest + # native actor ID + push20 0xff00000000000000000000000000000000000102 + extcodesize + %return_stack_word() + +placeholder: + jumpdest + # non-existent account ID + push20 0xff00000000000000000000000000000000000103 + extcodesize + %return_stack_word() + +non_existent: + jumpdest + # non-existent account ID + push20 0xff00000000000000000000000000000000000104 + extcodesize + %return_stack_word() + +"#; + + asm::new_contract("extcodesize", init, body).unwrap() + }; + + let mut rt = util::construct_and_verify(bytecode); + + // a fake CID + let bytecode_cid = Cid::try_from("baeaikaia").unwrap(); + let bytecode = vec![0x01, 0x02, 0x03, 0x04]; + rt.store.put_keyed(&bytecode_cid, bytecode.as_slice()).unwrap(); + + // 0x88 is an EVM actor + let evm_contract = FILAddress::new_id(0x88); + rt.set_address_actor_type(evm_contract, *EVM_ACTOR_CODE_ID); + + // 0x89 is a native actor + let native_actor = FILAddress::new_id(0x89); + rt.set_address_actor_type(native_actor, *DUMMY_ACTOR_CODE_ID); + + // 0x0101 is an EVM EOA account + let evm_account = FILAddress::new_id(0x0101); + // TODO this is part of the account abstraction hack, where placeholders are magically accounts + rt.set_address_actor_type(evm_account, *PLACEHOLDER_ACTOR_CODE_ID); + + // 0x0102 is a native account + let native_account = FILAddress::new_id(0x0102); + rt.set_address_actor_type(native_account, *ACCOUNT_ACTOR_CODE_ID); + + // 0x0103 is a placeholder account + let placeholder = FILAddress::new_id(0x0102); + rt.set_address_actor_type(placeholder, *PLACEHOLDER_ACTOR_CODE_ID); + + // evm actor + let method = util::dispatch_num_word(0); + let expected = U256::from(0x04); + { + rt.expect_send( + evm_contract, + evm::Method::GetBytecode as u64, + Default::default(), + TokenAmount::zero(), + None, + SendFlags::READ_ONLY, + IpldBlock::serialize_cbor(&bytecode_cid).unwrap(), + ExitCode::OK, + None, + ); + + let result = util::invoke_contract(&mut rt, &method); + rt.verify(); + assert_eq!(U256::from_big_endian(&result), expected); + rt.reset(); + } + + // native actor + let method = util::dispatch_num_word(1); + let expected = U256::from(0x01); + { + let result = util::invoke_contract(&mut rt, &method); + rt.verify(); + assert_eq!(U256::from_big_endian(&result), expected); + rt.reset(); + } + + // EVM account + let method = util::dispatch_num_word(2); + let expected = U256::from(0x00); + { + let result = util::invoke_contract(&mut rt, &method); + rt.verify(); + assert_eq!(U256::from_big_endian(&result), expected); + rt.reset(); + } + + // native account + let method = util::dispatch_num_word(3); + let expected = U256::from(0x00); + { + let result = util::invoke_contract(&mut rt, &method); + rt.verify(); + assert_eq!(U256::from_big_endian(&result), expected); + rt.reset(); + } + + // placeholder + let method = util::dispatch_num_word(5); + let expected = U256::from(0x00); + { + let result = util::invoke_contract(&mut rt, &method); + rt.verify(); + assert_eq!(U256::from_big_endian(&result), expected); + rt.reset(); + } + + // non existent + let method = util::dispatch_num_word(4); + let expected = U256::from(0x00); + { + let result = util::invoke_contract(&mut rt, &method); + rt.verify(); + assert_eq!(U256::from_big_endian(&result), expected); + rt.reset(); + } +} + +#[test] +fn test_extcodehash() { + let bytecode = { + let init = ""; + let body = r#" +%dispatch_begin() +%dispatch(0x00, evm_contract) +%dispatch(0x01, native_actor) +%dispatch(0x02, non_exist) +%dispatch(0x03, account) +%dispatch_end() + +evm_contract: + jumpdest + # get code hash of address 0x88 + push20 0xff00000000000000000000000000000000000088 + extcodehash + %return_stack_word() + +native_actor: + jumpdest + # get code hash of address 0x89 + push20 0xff00000000000000000000000000000000000089 + extcodehash + %return_stack_word() + +non_exist: + jumpdest + # get code hash of address 0xff + push20 0xff000000000000000000000000000000000000ff + extcodehash + %return_stack_word() + +account: + jumpdest + # get code hash of address 0x8A + push20 0xff0000000000000000000000000000000000008A + extcodehash + %return_stack_word() + +"#; + + asm::new_contract("extcodehash", init, body).unwrap() + }; + + let mut rt = util::construct_and_verify(bytecode); + + // 0x88 is an EVM actor + let evm_target = FILAddress::new_id(0x88); + rt.set_address_actor_type(evm_target, *EVM_ACTOR_CODE_ID); + + // 0x89 is an native actor + let native_target = FILAddress::new_id(0x89); + rt.set_address_actor_type(native_target, *DUMMY_ACTOR_CODE_ID); + + // 0x8A is an account + let native_target = FILAddress::new_id(0x8A); + rt.set_address_actor_type(native_target, *PLACEHOLDER_ACTOR_CODE_ID); + + let empty_hash = empty_bytecode_hash(&mut rt); + + // a random hash value + let bytecode = b"foo bar boxy"; + let bytecode_hash = + BytecodeHash::try_from(rt.hash(SupportedHashes::Keccak256, bytecode).as_slice()).unwrap(); + + rt.expect_send( + evm_target, + evm::Method::GetBytecodeHash as u64, + Default::default(), + TokenAmount::zero(), + None, + SendFlags::READ_ONLY, + IpldBlock::serialize_cbor(&bytecode_hash).unwrap(), + ExitCode::OK, + None, + ); + + // Evm code + let result = util::invoke_contract(&mut rt, &util::dispatch_num_word(0)); + rt.verify(); + assert_eq!(U256::from_big_endian(&result), U256::from(bytecode_hash)); + rt.reset(); + + // Native code is keccak256([0xfe]) + let result = util::invoke_contract(&mut rt, &util::dispatch_num_word(1)); + rt.verify(); + assert_eq!( + U256::from_big_endian(&result), + U256::from(rt.hash(SupportedHashes::Keccak256, &[0xfe]).as_slice()) + ); + rt.reset(); + + // Non-existing contracts are 0 + let result = util::invoke_contract(&mut rt, &util::dispatch_num_word(2)); + rt.verify(); + assert_eq!(U256::from_big_endian(&result), U256::from(0)); + rt.reset(); + + // _All_ existing accounts are empty hash + let result = util::invoke_contract(&mut rt, &util::dispatch_num_word(3)); + rt.verify(); + assert_eq!( + U256::from_big_endian(&result).to_bytes(), + empty_hash.as_slice(), + "expected empty hash: {}, got {}", + hex::encode(&empty_hash), + hex::encode(&result) + ); + rt.reset(); +} + +#[test] +fn test_getbytecodehash_method() { + let mut rt = util::construct_and_verify(Vec::new()); + rt.expect_validate_caller_any(); + + let res: BytecodeHash = rt + .call::(evm::Method::GetBytecodeHash as u64, None) + .unwrap() + .unwrap() + .deserialize() + .unwrap(); + assert_eq!(<[u8; 32]>::from(res), empty_bytecode_hash(&mut rt)) +} + +/// Keccak256 hash of &[] +fn empty_bytecode_hash(rt: &mut impl Runtime) -> [u8; 32] { + rt.hash(SupportedHashes::Keccak256, &[]).try_into().unwrap() +} + +#[test] +fn test_extcodecopy() { + let bytecode = { + let init = ""; + let body = r#" + +%dispatch_begin() +%dispatch(0x00, evm_contract) +%dispatch(0x01, native_actor) +%dispatch(0x02, invalid_address) +%dispatch(0x03, precompile) +%dispatch_end() + +evm_contract: + jumpdest + push1 0xff + push1 0x00 + push1 0x00 + push20 0xff00000000000000000000000000000000000088 + extcodecopy + # return 0x00..0x04 + push1 0x04 + push1 0x00 + return + +native_actor: + jumpdest + push1 0xff + push1 0x00 + push1 0x00 + push20 0xff00000000000000000000000000000000000089 + extcodecopy + # return 0x00..0x01 + push1 0x01 + push1 0x00 + return + +invalid_address: + jumpdest + push1 0xff + push1 0x00 + push1 0x00 + push20 0xff000000000000000000000000000000000000ff + extcodecopy + # return 0x00..0x20 + push1 0x20 + push1 0x00 + return + +precompile: + jumpdest + push1 0xff + push1 0x00 + push1 0x00 + # first precompile address + push20 0x0000000000000000000000000000000000000001 + extcodecopy + # return 0x00..0x20 + push1 0x20 + push1 0x00 + return + + "#; + + asm::new_contract("extcodecopy", init, body).unwrap() + }; + + let mut rt = util::construct_and_verify(bytecode); + + // 0x88 is an EVM actor + let evm_target = FILAddress::new_id(0x88); + rt.set_address_actor_type(evm_target, *EVM_ACTOR_CODE_ID); + + // 0x89 is a native actor + let native_target = FILAddress::new_id(0x89); + rt.set_address_actor_type(native_target, *DUMMY_ACTOR_CODE_ID); + + // a random CID + let bytecode_cid = Cid::try_from("baeaikaia").unwrap(); + let other_bytecode = vec![0x01, 0x02, 0x03, 0x04]; + rt.store.put_keyed(&bytecode_cid, other_bytecode.as_slice()).unwrap(); + + rt.expect_send( + evm_target, + evm::Method::GetBytecode as u64, + Default::default(), + TokenAmount::zero(), + None, + SendFlags::READ_ONLY, + IpldBlock::serialize_cbor(&bytecode_cid).unwrap(), + ExitCode::OK, + None, + ); + + let result = util::invoke_contract(&mut rt, &util::dispatch_num_word(0)); + rt.verify(); + assert_eq!(other_bytecode.as_slice(), result); + rt.reset(); + + // calling code copy on native actors return "invalid" instruction from EIP-141 + let result = util::invoke_contract(&mut rt, &util::dispatch_num_word(1)); + rt.verify(); + assert_eq!(vec![0xFE], result); + rt.reset(); + + // invalid addresses are flattened + let result = util::invoke_contract(&mut rt, &util::dispatch_num_word(2)); + rt.verify(); + assert_eq!(U256::from_big_endian(&result), U256::from(0)); + + // precompile addresses are flattened + let result = util::invoke_contract(&mut rt, &util::dispatch_num_word(3)); + rt.verify(); + assert_eq!(U256::from_big_endian(&result), U256::from(0)); +} + +#[test] +fn test_ext_in_initcode() { + let bytecode = { + let init = " + +# code hash of self +push20 0xFEEDFACECAFEBEEF000000000000000000000000 +extcodehash +push1 0x00 # key +sstore # store for later + +# code size of self +push20 0xFEEDFACECAFEBEEF000000000000000000000000 +extcodesize +push1 0x01 # key +sstore # store for later + "; + + let body = r#" + +%dispatch_begin() +%dispatch(0x00, init_exthash) +%dispatch(0x01, init_extsize) +%dispatch_end() + +init_exthash: + jumpdest + push1 0x00 + sload + %return_stack_word() + +init_extsize: + jumpdest + push1 0x01 + sload + %return_stack_word() + "#; + + asm::new_contract("ext_initcode", init, body).unwrap() + }; + + let mut rt = util::init_construct_and_verify(bytecode, |rt| { + rt.expect_send( + CONTRACT_ID, + evm::Method::GetBytecodeHash as u64, + Default::default(), + TokenAmount::zero(), + None, + SendFlags::READ_ONLY, + IpldBlock::serialize_cbor(&BytecodeHash::EMPTY).unwrap(), + ExitCode::OK, + None, + ); + + rt.store.put_keyed(&EMPTY_ARR_CID, &[]).unwrap(); + rt.expect_send( + CONTRACT_ID, + evm::Method::GetBytecode as u64, + Default::default(), + TokenAmount::zero(), + None, + SendFlags::READ_ONLY, + IpldBlock::serialize_cbor(&EMPTY_ARR_CID).unwrap(), + ExitCode::OK, + None, + ); + }); + + // codehash + let result = util::invoke_contract(&mut rt, &util::dispatch_num_word(0)); + rt.verify(); + assert_eq!(BytecodeHash::EMPTY.as_slice(), result); + + // codesize + let result = util::invoke_contract(&mut rt, &util::dispatch_num_word(1)); + rt.verify(); + assert_eq!(U256::zero(), U256::from_big_endian(&result)); +} diff --git a/actors/evm/tests/measurements/array_push_n1.jsonline b/actors/evm/tests/measurements/array_push_n1.jsonline new file mode 100644 index 000000000..eb6aa57bb --- /dev/null +++ b/actors/evm/tests/measurements/array_push_n1.jsonline @@ -0,0 +1,200 @@ +{"i":0,"series":1,"stats":{"get_bytes":2132,"get_count":3,"put_bytes":2311,"put_count":5}} +{"i":1,"series":1,"stats":{"get_bytes":2185,"get_count":3,"put_bytes":183,"put_count":2}} +{"i":2,"series":1,"stats":{"get_bytes":2189,"get_count":3,"put_bytes":187,"put_count":2}} +{"i":3,"series":1,"stats":{"get_bytes":2193,"get_count":3,"put_bytes":191,"put_count":2}} +{"i":4,"series":1,"stats":{"get_bytes":2197,"get_count":3,"put_bytes":195,"put_count":2}} +{"i":5,"series":1,"stats":{"get_bytes":2201,"get_count":3,"put_bytes":199,"put_count":2}} +{"i":6,"series":1,"stats":{"get_bytes":2205,"get_count":3,"put_bytes":204,"put_count":2}} +{"i":7,"series":1,"stats":{"get_bytes":2210,"get_count":3,"put_bytes":208,"put_count":2}} +{"i":8,"series":1,"stats":{"get_bytes":2214,"get_count":3,"put_bytes":336,"put_count":3}} +{"i":9,"series":1,"stats":{"get_bytes":2342,"get_count":4,"put_bytes":340,"put_count":3}} +{"i":10,"series":1,"stats":{"get_bytes":2346,"get_count":4,"put_bytes":344,"put_count":3}} +{"i":11,"series":1,"stats":{"get_bytes":2350,"get_count":4,"put_bytes":348,"put_count":3}} +{"i":12,"series":1,"stats":{"get_bytes":2354,"get_count":4,"put_bytes":352,"put_count":3}} +{"i":13,"series":1,"stats":{"get_bytes":2358,"get_count":4,"put_bytes":356,"put_count":3}} +{"i":14,"series":1,"stats":{"get_bytes":2362,"get_count":4,"put_bytes":361,"put_count":3}} +{"i":15,"series":1,"stats":{"get_bytes":2367,"get_count":4,"put_bytes":365,"put_count":3}} +{"i":16,"series":1,"stats":{"get_bytes":2371,"get_count":4,"put_bytes":317,"put_count":3}} +{"i":17,"series":1,"stats":{"get_bytes":2323,"get_count":4,"put_bytes":321,"put_count":3}} +{"i":18,"series":1,"stats":{"get_bytes":2327,"get_count":4,"put_bytes":325,"put_count":3}} +{"i":19,"series":1,"stats":{"get_bytes":2331,"get_count":4,"put_bytes":329,"put_count":3}} +{"i":20,"series":1,"stats":{"get_bytes":2335,"get_count":4,"put_bytes":333,"put_count":3}} +{"i":21,"series":1,"stats":{"get_bytes":2339,"get_count":4,"put_bytes":337,"put_count":3}} +{"i":22,"series":1,"stats":{"get_bytes":2343,"get_count":4,"put_bytes":342,"put_count":3}} +{"i":23,"series":1,"stats":{"get_bytes":2348,"get_count":4,"put_bytes":346,"put_count":3}} +{"i":24,"series":1,"stats":{"get_bytes":2352,"get_count":4,"put_bytes":440,"put_count":4}} +{"i":25,"series":1,"stats":{"get_bytes":2446,"get_count":5,"put_bytes":444,"put_count":4}} +{"i":26,"series":1,"stats":{"get_bytes":2450,"get_count":5,"put_bytes":448,"put_count":4}} +{"i":27,"series":1,"stats":{"get_bytes":2454,"get_count":5,"put_bytes":452,"put_count":4}} +{"i":28,"series":1,"stats":{"get_bytes":2458,"get_count":5,"put_bytes":456,"put_count":4}} +{"i":29,"series":1,"stats":{"get_bytes":2462,"get_count":5,"put_bytes":460,"put_count":4}} +{"i":30,"series":1,"stats":{"get_bytes":2466,"get_count":5,"put_bytes":465,"put_count":4}} +{"i":31,"series":1,"stats":{"get_bytes":2471,"get_count":5,"put_bytes":469,"put_count":4}} +{"i":32,"series":1,"stats":{"get_bytes":2331,"get_count":4,"put_bytes":366,"put_count":3}} +{"i":33,"series":1,"stats":{"get_bytes":2372,"get_count":4,"put_bytes":370,"put_count":3}} +{"i":34,"series":1,"stats":{"get_bytes":2376,"get_count":4,"put_bytes":374,"put_count":3}} +{"i":35,"series":1,"stats":{"get_bytes":2380,"get_count":4,"put_bytes":378,"put_count":3}} +{"i":36,"series":1,"stats":{"get_bytes":2384,"get_count":4,"put_bytes":382,"put_count":3}} +{"i":37,"series":1,"stats":{"get_bytes":2388,"get_count":4,"put_bytes":386,"put_count":3}} +{"i":38,"series":1,"stats":{"get_bytes":2392,"get_count":4,"put_bytes":391,"put_count":3}} +{"i":39,"series":1,"stats":{"get_bytes":2397,"get_count":4,"put_bytes":395,"put_count":3}} +{"i":40,"series":1,"stats":{"get_bytes":2401,"get_count":4,"put_bytes":489,"put_count":4}} +{"i":41,"series":1,"stats":{"get_bytes":2495,"get_count":5,"put_bytes":493,"put_count":4}} +{"i":42,"series":1,"stats":{"get_bytes":2499,"get_count":5,"put_bytes":497,"put_count":4}} +{"i":43,"series":1,"stats":{"get_bytes":2503,"get_count":5,"put_bytes":501,"put_count":4}} +{"i":44,"series":1,"stats":{"get_bytes":2507,"get_count":5,"put_bytes":505,"put_count":4}} +{"i":45,"series":1,"stats":{"get_bytes":2511,"get_count":5,"put_bytes":509,"put_count":4}} +{"i":46,"series":1,"stats":{"get_bytes":2515,"get_count":5,"put_bytes":514,"put_count":4}} +{"i":47,"series":1,"stats":{"get_bytes":2520,"get_count":5,"put_bytes":518,"put_count":4}} +{"i":48,"series":1,"stats":{"get_bytes":2380,"get_count":4,"put_bytes":415,"put_count":3}} +{"i":49,"series":1,"stats":{"get_bytes":2421,"get_count":4,"put_bytes":419,"put_count":3}} +{"i":50,"series":1,"stats":{"get_bytes":2425,"get_count":4,"put_bytes":423,"put_count":3}} +{"i":51,"series":1,"stats":{"get_bytes":2429,"get_count":4,"put_bytes":427,"put_count":3}} +{"i":52,"series":1,"stats":{"get_bytes":2433,"get_count":4,"put_bytes":431,"put_count":3}} +{"i":53,"series":1,"stats":{"get_bytes":2437,"get_count":4,"put_bytes":435,"put_count":3}} +{"i":54,"series":1,"stats":{"get_bytes":2441,"get_count":4,"put_bytes":440,"put_count":3}} +{"i":55,"series":1,"stats":{"get_bytes":2446,"get_count":4,"put_bytes":444,"put_count":3}} +{"i":56,"series":1,"stats":{"get_bytes":2450,"get_count":4,"put_bytes":538,"put_count":4}} +{"i":57,"series":1,"stats":{"get_bytes":2544,"get_count":5,"put_bytes":542,"put_count":4}} +{"i":58,"series":1,"stats":{"get_bytes":2548,"get_count":5,"put_bytes":546,"put_count":4}} +{"i":59,"series":1,"stats":{"get_bytes":2552,"get_count":5,"put_bytes":550,"put_count":4}} +{"i":60,"series":1,"stats":{"get_bytes":2556,"get_count":5,"put_bytes":554,"put_count":4}} +{"i":61,"series":1,"stats":{"get_bytes":2560,"get_count":5,"put_bytes":558,"put_count":4}} +{"i":62,"series":1,"stats":{"get_bytes":2564,"get_count":5,"put_bytes":563,"put_count":4}} +{"i":63,"series":1,"stats":{"get_bytes":2569,"get_count":5,"put_bytes":567,"put_count":4}} +{"i":64,"series":1,"stats":{"get_bytes":2429,"get_count":4,"put_bytes":464,"put_count":3}} +{"i":65,"series":1,"stats":{"get_bytes":2470,"get_count":4,"put_bytes":468,"put_count":3}} +{"i":66,"series":1,"stats":{"get_bytes":2474,"get_count":4,"put_bytes":472,"put_count":3}} +{"i":67,"series":1,"stats":{"get_bytes":2478,"get_count":4,"put_bytes":476,"put_count":3}} +{"i":68,"series":1,"stats":{"get_bytes":2482,"get_count":4,"put_bytes":480,"put_count":3}} +{"i":69,"series":1,"stats":{"get_bytes":2486,"get_count":4,"put_bytes":484,"put_count":3}} +{"i":70,"series":1,"stats":{"get_bytes":2490,"get_count":4,"put_bytes":489,"put_count":3}} +{"i":71,"series":1,"stats":{"get_bytes":2495,"get_count":4,"put_bytes":493,"put_count":3}} +{"i":72,"series":1,"stats":{"get_bytes":2499,"get_count":4,"put_bytes":587,"put_count":4}} +{"i":73,"series":1,"stats":{"get_bytes":2593,"get_count":5,"put_bytes":591,"put_count":4}} +{"i":74,"series":1,"stats":{"get_bytes":2597,"get_count":5,"put_bytes":595,"put_count":4}} +{"i":75,"series":1,"stats":{"get_bytes":2601,"get_count":5,"put_bytes":599,"put_count":4}} +{"i":76,"series":1,"stats":{"get_bytes":2605,"get_count":5,"put_bytes":603,"put_count":4}} +{"i":77,"series":1,"stats":{"get_bytes":2609,"get_count":5,"put_bytes":607,"put_count":4}} +{"i":78,"series":1,"stats":{"get_bytes":2613,"get_count":5,"put_bytes":612,"put_count":4}} +{"i":79,"series":1,"stats":{"get_bytes":2618,"get_count":5,"put_bytes":616,"put_count":4}} +{"i":80,"series":1,"stats":{"get_bytes":2478,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":81,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":319,"put_count":3}} +{"i":82,"series":1,"stats":{"get_bytes":2325,"get_count":4,"put_bytes":323,"put_count":3}} +{"i":83,"series":1,"stats":{"get_bytes":2329,"get_count":4,"put_bytes":327,"put_count":3}} +{"i":84,"series":1,"stats":{"get_bytes":2333,"get_count":4,"put_bytes":331,"put_count":3}} +{"i":85,"series":1,"stats":{"get_bytes":2337,"get_count":4,"put_bytes":335,"put_count":3}} +{"i":86,"series":1,"stats":{"get_bytes":2341,"get_count":4,"put_bytes":340,"put_count":3}} +{"i":87,"series":1,"stats":{"get_bytes":2346,"get_count":4,"put_bytes":344,"put_count":3}} +{"i":88,"series":1,"stats":{"get_bytes":2350,"get_count":4,"put_bytes":439,"put_count":4}} +{"i":89,"series":1,"stats":{"get_bytes":2445,"get_count":5,"put_bytes":443,"put_count":4}} +{"i":90,"series":1,"stats":{"get_bytes":2449,"get_count":5,"put_bytes":447,"put_count":4}} +{"i":91,"series":1,"stats":{"get_bytes":2453,"get_count":5,"put_bytes":451,"put_count":4}} +{"i":92,"series":1,"stats":{"get_bytes":2457,"get_count":5,"put_bytes":455,"put_count":4}} +{"i":93,"series":1,"stats":{"get_bytes":2461,"get_count":5,"put_bytes":459,"put_count":4}} +{"i":94,"series":1,"stats":{"get_bytes":2465,"get_count":5,"put_bytes":464,"put_count":4}} +{"i":95,"series":1,"stats":{"get_bytes":2470,"get_count":5,"put_bytes":468,"put_count":4}} +{"i":96,"series":1,"stats":{"get_bytes":2474,"get_count":5,"put_bytes":417,"put_count":4}} +{"i":97,"series":1,"stats":{"get_bytes":2423,"get_count":5,"put_bytes":421,"put_count":4}} +{"i":98,"series":1,"stats":{"get_bytes":2427,"get_count":5,"put_bytes":425,"put_count":4}} +{"i":99,"series":1,"stats":{"get_bytes":2431,"get_count":5,"put_bytes":429,"put_count":4}} +{"i":0,"series":2,"stats":{"get_bytes":2225,"get_count":3,"put_bytes":355,"put_count":3}} +{"i":1,"series":2,"stats":{"get_bytes":2361,"get_count":4,"put_bytes":359,"put_count":3}} +{"i":2,"series":2,"stats":{"get_bytes":2365,"get_count":4,"put_bytes":363,"put_count":3}} +{"i":3,"series":2,"stats":{"get_bytes":2369,"get_count":4,"put_bytes":367,"put_count":3}} +{"i":4,"series":2,"stats":{"get_bytes":2373,"get_count":4,"put_bytes":371,"put_count":3}} +{"i":5,"series":2,"stats":{"get_bytes":2377,"get_count":4,"put_bytes":375,"put_count":3}} +{"i":6,"series":2,"stats":{"get_bytes":2381,"get_count":4,"put_bytes":380,"put_count":3}} +{"i":7,"series":2,"stats":{"get_bytes":2386,"get_count":4,"put_bytes":384,"put_count":3}} +{"i":8,"series":2,"stats":{"get_bytes":2390,"get_count":4,"put_bytes":512,"put_count":4}} +{"i":9,"series":2,"stats":{"get_bytes":2518,"get_count":5,"put_bytes":516,"put_count":4}} +{"i":10,"series":2,"stats":{"get_bytes":2522,"get_count":5,"put_bytes":520,"put_count":4}} +{"i":11,"series":2,"stats":{"get_bytes":2526,"get_count":5,"put_bytes":524,"put_count":4}} +{"i":12,"series":2,"stats":{"get_bytes":2530,"get_count":5,"put_bytes":528,"put_count":4}} +{"i":13,"series":2,"stats":{"get_bytes":2534,"get_count":5,"put_bytes":532,"put_count":4}} +{"i":14,"series":2,"stats":{"get_bytes":2538,"get_count":5,"put_bytes":537,"put_count":4}} +{"i":15,"series":2,"stats":{"get_bytes":2543,"get_count":5,"put_bytes":541,"put_count":4}} +{"i":16,"series":2,"stats":{"get_bytes":2547,"get_count":5,"put_bytes":491,"put_count":4}} +{"i":17,"series":2,"stats":{"get_bytes":2497,"get_count":5,"put_bytes":495,"put_count":4}} +{"i":18,"series":2,"stats":{"get_bytes":2501,"get_count":5,"put_bytes":499,"put_count":4}} +{"i":19,"series":2,"stats":{"get_bytes":2505,"get_count":5,"put_bytes":503,"put_count":4}} +{"i":20,"series":2,"stats":{"get_bytes":2509,"get_count":5,"put_bytes":507,"put_count":4}} +{"i":21,"series":2,"stats":{"get_bytes":2513,"get_count":5,"put_bytes":511,"put_count":4}} +{"i":22,"series":2,"stats":{"get_bytes":2517,"get_count":5,"put_bytes":516,"put_count":4}} +{"i":23,"series":2,"stats":{"get_bytes":2522,"get_count":5,"put_bytes":520,"put_count":4}} +{"i":24,"series":2,"stats":{"get_bytes":2526,"get_count":5,"put_bytes":614,"put_count":5}} +{"i":25,"series":2,"stats":{"get_bytes":2620,"get_count":6,"put_bytes":618,"put_count":5}} +{"i":26,"series":2,"stats":{"get_bytes":2624,"get_count":6,"put_bytes":622,"put_count":5}} +{"i":27,"series":2,"stats":{"get_bytes":2628,"get_count":6,"put_bytes":626,"put_count":5}} +{"i":28,"series":2,"stats":{"get_bytes":2632,"get_count":6,"put_bytes":630,"put_count":5}} +{"i":29,"series":2,"stats":{"get_bytes":2636,"get_count":6,"put_bytes":634,"put_count":5}} +{"i":30,"series":2,"stats":{"get_bytes":2640,"get_count":6,"put_bytes":639,"put_count":5}} +{"i":31,"series":2,"stats":{"get_bytes":2645,"get_count":6,"put_bytes":643,"put_count":5}} +{"i":32,"series":2,"stats":{"get_bytes":2505,"get_count":5,"put_bytes":540,"put_count":4}} +{"i":33,"series":2,"stats":{"get_bytes":2546,"get_count":5,"put_bytes":544,"put_count":4}} +{"i":34,"series":2,"stats":{"get_bytes":2550,"get_count":5,"put_bytes":548,"put_count":4}} +{"i":35,"series":2,"stats":{"get_bytes":2554,"get_count":5,"put_bytes":552,"put_count":4}} +{"i":36,"series":2,"stats":{"get_bytes":2558,"get_count":5,"put_bytes":556,"put_count":4}} +{"i":37,"series":2,"stats":{"get_bytes":2562,"get_count":5,"put_bytes":560,"put_count":4}} +{"i":38,"series":2,"stats":{"get_bytes":2566,"get_count":5,"put_bytes":565,"put_count":4}} +{"i":39,"series":2,"stats":{"get_bytes":2571,"get_count":5,"put_bytes":569,"put_count":4}} +{"i":40,"series":2,"stats":{"get_bytes":2575,"get_count":5,"put_bytes":663,"put_count":5}} +{"i":41,"series":2,"stats":{"get_bytes":2669,"get_count":6,"put_bytes":667,"put_count":5}} +{"i":42,"series":2,"stats":{"get_bytes":2673,"get_count":6,"put_bytes":671,"put_count":5}} +{"i":43,"series":2,"stats":{"get_bytes":2677,"get_count":6,"put_bytes":675,"put_count":5}} +{"i":44,"series":2,"stats":{"get_bytes":2681,"get_count":6,"put_bytes":679,"put_count":5}} +{"i":45,"series":2,"stats":{"get_bytes":2685,"get_count":6,"put_bytes":683,"put_count":5}} +{"i":46,"series":2,"stats":{"get_bytes":2689,"get_count":6,"put_bytes":688,"put_count":5}} +{"i":47,"series":2,"stats":{"get_bytes":2694,"get_count":6,"put_bytes":692,"put_count":5}} +{"i":48,"series":2,"stats":{"get_bytes":2554,"get_count":5,"put_bytes":589,"put_count":4}} +{"i":49,"series":2,"stats":{"get_bytes":2595,"get_count":5,"put_bytes":593,"put_count":4}} +{"i":50,"series":2,"stats":{"get_bytes":2599,"get_count":5,"put_bytes":597,"put_count":4}} +{"i":51,"series":2,"stats":{"get_bytes":2603,"get_count":5,"put_bytes":601,"put_count":4}} +{"i":52,"series":2,"stats":{"get_bytes":2607,"get_count":5,"put_bytes":605,"put_count":4}} +{"i":53,"series":2,"stats":{"get_bytes":2611,"get_count":5,"put_bytes":609,"put_count":4}} +{"i":54,"series":2,"stats":{"get_bytes":2615,"get_count":5,"put_bytes":614,"put_count":4}} +{"i":55,"series":2,"stats":{"get_bytes":2620,"get_count":5,"put_bytes":618,"put_count":4}} +{"i":56,"series":2,"stats":{"get_bytes":2624,"get_count":5,"put_bytes":712,"put_count":5}} +{"i":57,"series":2,"stats":{"get_bytes":2718,"get_count":6,"put_bytes":716,"put_count":5}} +{"i":58,"series":2,"stats":{"get_bytes":2722,"get_count":6,"put_bytes":720,"put_count":5}} +{"i":59,"series":2,"stats":{"get_bytes":2726,"get_count":6,"put_bytes":724,"put_count":5}} +{"i":60,"series":2,"stats":{"get_bytes":2730,"get_count":6,"put_bytes":728,"put_count":5}} +{"i":61,"series":2,"stats":{"get_bytes":2734,"get_count":6,"put_bytes":732,"put_count":5}} +{"i":62,"series":2,"stats":{"get_bytes":2738,"get_count":6,"put_bytes":737,"put_count":5}} +{"i":63,"series":2,"stats":{"get_bytes":2743,"get_count":6,"put_bytes":741,"put_count":5}} +{"i":64,"series":2,"stats":{"get_bytes":2603,"get_count":5,"put_bytes":638,"put_count":4}} +{"i":65,"series":2,"stats":{"get_bytes":2644,"get_count":5,"put_bytes":642,"put_count":4}} +{"i":66,"series":2,"stats":{"get_bytes":2648,"get_count":5,"put_bytes":646,"put_count":4}} +{"i":67,"series":2,"stats":{"get_bytes":2652,"get_count":5,"put_bytes":650,"put_count":4}} +{"i":68,"series":2,"stats":{"get_bytes":2656,"get_count":5,"put_bytes":654,"put_count":4}} +{"i":69,"series":2,"stats":{"get_bytes":2660,"get_count":5,"put_bytes":658,"put_count":4}} +{"i":70,"series":2,"stats":{"get_bytes":2664,"get_count":5,"put_bytes":663,"put_count":4}} +{"i":71,"series":2,"stats":{"get_bytes":2669,"get_count":5,"put_bytes":667,"put_count":4}} +{"i":72,"series":2,"stats":{"get_bytes":2673,"get_count":5,"put_bytes":761,"put_count":5}} +{"i":73,"series":2,"stats":{"get_bytes":2767,"get_count":6,"put_bytes":765,"put_count":5}} +{"i":74,"series":2,"stats":{"get_bytes":2771,"get_count":6,"put_bytes":769,"put_count":5}} +{"i":75,"series":2,"stats":{"get_bytes":2775,"get_count":6,"put_bytes":773,"put_count":5}} +{"i":76,"series":2,"stats":{"get_bytes":2779,"get_count":6,"put_bytes":777,"put_count":5}} +{"i":77,"series":2,"stats":{"get_bytes":2783,"get_count":6,"put_bytes":781,"put_count":5}} +{"i":78,"series":2,"stats":{"get_bytes":2787,"get_count":6,"put_bytes":786,"put_count":5}} +{"i":79,"series":2,"stats":{"get_bytes":2792,"get_count":6,"put_bytes":790,"put_count":5}} +{"i":80,"series":2,"stats":{"get_bytes":2652,"get_count":5,"put_bytes":687,"put_count":4}} +{"i":81,"series":2,"stats":{"get_bytes":2693,"get_count":5,"put_bytes":691,"put_count":4}} +{"i":82,"series":2,"stats":{"get_bytes":2697,"get_count":5,"put_bytes":695,"put_count":4}} +{"i":83,"series":2,"stats":{"get_bytes":2701,"get_count":5,"put_bytes":699,"put_count":4}} +{"i":84,"series":2,"stats":{"get_bytes":2705,"get_count":5,"put_bytes":703,"put_count":4}} +{"i":85,"series":2,"stats":{"get_bytes":2709,"get_count":5,"put_bytes":707,"put_count":4}} +{"i":86,"series":2,"stats":{"get_bytes":2713,"get_count":5,"put_bytes":712,"put_count":4}} +{"i":87,"series":2,"stats":{"get_bytes":2718,"get_count":5,"put_bytes":716,"put_count":4}} +{"i":88,"series":2,"stats":{"get_bytes":2722,"get_count":5,"put_bytes":810,"put_count":5}} +{"i":89,"series":2,"stats":{"get_bytes":2816,"get_count":6,"put_bytes":814,"put_count":5}} +{"i":90,"series":2,"stats":{"get_bytes":2820,"get_count":6,"put_bytes":818,"put_count":5}} +{"i":91,"series":2,"stats":{"get_bytes":2824,"get_count":6,"put_bytes":822,"put_count":5}} +{"i":92,"series":2,"stats":{"get_bytes":2828,"get_count":6,"put_bytes":826,"put_count":5}} +{"i":93,"series":2,"stats":{"get_bytes":2832,"get_count":6,"put_bytes":830,"put_count":5}} +{"i":94,"series":2,"stats":{"get_bytes":2836,"get_count":6,"put_bytes":835,"put_count":5}} +{"i":95,"series":2,"stats":{"get_bytes":2841,"get_count":6,"put_bytes":839,"put_count":5}} +{"i":96,"series":2,"stats":{"get_bytes":2701,"get_count":5,"put_bytes":736,"put_count":4}} +{"i":97,"series":2,"stats":{"get_bytes":2742,"get_count":5,"put_bytes":740,"put_count":4}} +{"i":98,"series":2,"stats":{"get_bytes":2746,"get_count":5,"put_bytes":744,"put_count":4}} +{"i":99,"series":2,"stats":{"get_bytes":2750,"get_count":5,"put_bytes":748,"put_count":4}} diff --git a/actors/evm/tests/measurements/array_push_n1.png b/actors/evm/tests/measurements/array_push_n1.png new file mode 100644 index 000000000..9762c18e7 Binary files /dev/null and b/actors/evm/tests/measurements/array_push_n1.png differ diff --git a/actors/evm/tests/measurements/array_push_n100.jsonline b/actors/evm/tests/measurements/array_push_n100.jsonline new file mode 100644 index 000000000..92ece06cb --- /dev/null +++ b/actors/evm/tests/measurements/array_push_n100.jsonline @@ -0,0 +1,200 @@ +{"i":0,"series":1,"stats":{"get_bytes":2132,"get_count":3,"put_bytes":3677,"put_count":14}} +{"i":1,"series":1,"stats":{"get_bytes":2435,"get_count":5,"put_bytes":1604,"put_count":10}} +{"i":2,"series":1,"stats":{"get_bytes":2746,"get_count":5,"put_bytes":2006,"put_count":11}} +{"i":3,"series":1,"stats":{"get_bytes":3148,"get_count":6,"put_bytes":2318,"put_count":11}} +{"i":4,"series":1,"stats":{"get_bytes":3316,"get_count":5,"put_bytes":2523,"put_count":10}} +{"i":5,"series":1,"stats":{"get_bytes":3665,"get_count":5,"put_bytes":2834,"put_count":10}} +{"i":6,"series":1,"stats":{"get_bytes":2400,"get_count":4,"put_bytes":1711,"put_count":11}} +{"i":7,"series":1,"stats":{"get_bytes":2853,"get_count":6,"put_bytes":2023,"put_count":11}} +{"i":8,"series":1,"stats":{"get_bytes":3021,"get_count":5,"put_bytes":2227,"put_count":10}} +{"i":9,"series":1,"stats":{"get_bytes":3369,"get_count":5,"put_bytes":2540,"put_count":10}} +{"i":10,"series":1,"stats":{"get_bytes":3682,"get_count":5,"put_bytes":2940,"put_count":11}} +{"i":11,"series":1,"stats":{"get_bytes":4082,"get_count":6,"put_bytes":3304,"put_count":12}} +{"i":12,"series":1,"stats":{"get_bytes":2726,"get_count":5,"put_bytes":1932,"put_count":10}} +{"i":13,"series":1,"stats":{"get_bytes":3074,"get_count":5,"put_bytes":2244,"put_count":10}} +{"i":14,"series":1,"stats":{"get_bytes":3386,"get_count":5,"put_bytes":2646,"put_count":11}} +{"i":15,"series":1,"stats":{"get_bytes":3788,"get_count":6,"put_bytes":2957,"put_count":11}} +{"i":16,"series":1,"stats":{"get_bytes":3955,"get_count":5,"put_bytes":3213,"put_count":11}} +{"i":17,"series":1,"stats":{"get_bytes":2779,"get_count":5,"put_bytes":1949,"put_count":10}} +{"i":18,"series":1,"stats":{"get_bytes":3091,"get_count":5,"put_bytes":2350,"put_count":11}} +{"i":19,"series":1,"stats":{"get_bytes":3492,"get_count":6,"put_bytes":2662,"put_count":11}} +{"i":20,"series":1,"stats":{"get_bytes":3660,"get_count":5,"put_bytes":2866,"put_count":10}} +{"i":21,"series":1,"stats":{"get_bytes":4008,"get_count":5,"put_bytes":3231,"put_count":11}} +{"i":22,"series":1,"stats":{"get_bytes":2797,"get_count":5,"put_bytes":2056,"put_count":11}} +{"i":23,"series":1,"stats":{"get_bytes":3198,"get_count":6,"put_bytes":2368,"put_count":11}} +{"i":24,"series":1,"stats":{"get_bytes":3366,"get_count":5,"put_bytes":2572,"put_count":10}} +{"i":25,"series":1,"stats":{"get_bytes":3714,"get_count":5,"put_bytes":2884,"put_count":10}} +{"i":26,"series":1,"stats":{"get_bytes":4026,"get_count":5,"put_bytes":3337,"put_count":12}} +{"i":27,"series":1,"stats":{"get_bytes":2903,"get_count":6,"put_bytes":2073,"put_count":11}} +{"i":28,"series":1,"stats":{"get_bytes":3071,"get_count":5,"put_bytes":2277,"put_count":10}} +{"i":29,"series":1,"stats":{"get_bytes":3419,"get_count":5,"put_bytes":2588,"put_count":10}} +{"i":30,"series":1,"stats":{"get_bytes":3730,"get_count":5,"put_bytes":2990,"put_count":11}} +{"i":31,"series":1,"stats":{"get_bytes":4132,"get_count":6,"put_bytes":3354,"put_count":12}} +{"i":32,"series":1,"stats":{"get_bytes":2776,"get_count":5,"put_bytes":1982,"put_count":10}} +{"i":33,"series":1,"stats":{"get_bytes":3124,"get_count":5,"put_bytes":2293,"put_count":10}} +{"i":34,"series":1,"stats":{"get_bytes":3435,"get_count":5,"put_bytes":2694,"put_count":11}} +{"i":35,"series":1,"stats":{"get_bytes":3836,"get_count":6,"put_bytes":3007,"put_count":11}} +{"i":36,"series":1,"stats":{"get_bytes":4005,"get_count":5,"put_bytes":3263,"put_count":11}} +{"i":37,"series":1,"stats":{"get_bytes":2829,"get_count":5,"put_bytes":1999,"put_count":10}} +{"i":38,"series":1,"stats":{"get_bytes":3141,"get_count":5,"put_bytes":2399,"put_count":11}} +{"i":39,"series":1,"stats":{"get_bytes":3541,"get_count":6,"put_bytes":2711,"put_count":11}} +{"i":40,"series":1,"stats":{"get_bytes":3709,"get_count":5,"put_bytes":2916,"put_count":10}} +{"i":41,"series":1,"stats":{"get_bytes":4058,"get_count":5,"put_bytes":3280,"put_count":11}} +{"i":42,"series":1,"stats":{"get_bytes":2846,"get_count":5,"put_bytes":2104,"put_count":11}} +{"i":43,"series":1,"stats":{"get_bytes":3246,"get_count":6,"put_bytes":2416,"put_count":11}} +{"i":44,"series":1,"stats":{"get_bytes":3414,"get_count":5,"put_bytes":2620,"put_count":10}} +{"i":45,"series":1,"stats":{"get_bytes":3762,"get_count":5,"put_bytes":2933,"put_count":10}} +{"i":46,"series":1,"stats":{"get_bytes":4075,"get_count":5,"put_bytes":3334,"put_count":11}} +{"i":47,"series":1,"stats":{"get_bytes":2900,"get_count":5,"put_bytes":2121,"put_count":11}} +{"i":48,"series":1,"stats":{"get_bytes":3119,"get_count":5,"put_bytes":2325,"put_count":10}} +{"i":49,"series":1,"stats":{"get_bytes":3467,"get_count":5,"put_bytes":2637,"put_count":10}} +{"i":50,"series":1,"stats":{"get_bytes":3779,"get_count":5,"put_bytes":3039,"put_count":11}} +{"i":51,"series":1,"stats":{"get_bytes":4181,"get_count":6,"put_bytes":3350,"put_count":11}} +{"i":52,"series":1,"stats":{"get_bytes":2772,"get_count":4,"put_bytes":2030,"put_count":10}} +{"i":53,"series":1,"stats":{"get_bytes":3172,"get_count":5,"put_bytes":2342,"put_count":10}} +{"i":54,"series":1,"stats":{"get_bytes":3484,"get_count":5,"put_bytes":2743,"put_count":11}} +{"i":55,"series":1,"stats":{"get_bytes":3885,"get_count":6,"put_bytes":3056,"put_count":11}} +{"i":56,"series":1,"stats":{"get_bytes":4054,"get_count":5,"put_bytes":3259,"put_count":10}} +{"i":57,"series":1,"stats":{"get_bytes":4401,"get_count":5,"put_bytes":3623,"put_count":11}} +{"i":58,"series":1,"stats":{"get_bytes":3189,"get_count":5,"put_bytes":2448,"put_count":11}} +{"i":59,"series":1,"stats":{"get_bytes":3590,"get_count":6,"put_bytes":2760,"put_count":11}} +{"i":60,"series":1,"stats":{"get_bytes":3758,"get_count":5,"put_bytes":2965,"put_count":10}} +{"i":61,"series":1,"stats":{"get_bytes":4107,"get_count":5,"put_bytes":3276,"put_count":10}} +{"i":62,"series":1,"stats":{"get_bytes":4418,"get_count":5,"put_bytes":3783,"put_count":13}} +{"i":63,"series":1,"stats":{"get_bytes":2705,"get_count":6,"put_bytes":1875,"put_count":11}} +{"i":64,"series":1,"stats":{"get_bytes":2873,"get_count":5,"put_bytes":2079,"put_count":10}} +{"i":65,"series":1,"stats":{"get_bytes":3221,"get_count":5,"put_bytes":2391,"put_count":10}} +{"i":66,"series":1,"stats":{"get_bytes":3533,"get_count":5,"put_bytes":2792,"put_count":11}} +{"i":67,"series":1,"stats":{"get_bytes":3934,"get_count":6,"put_bytes":3208,"put_count":13}} +{"i":68,"series":1,"stats":{"get_bytes":2630,"get_count":6,"put_bytes":1836,"put_count":11}} +{"i":69,"series":1,"stats":{"get_bytes":2978,"get_count":6,"put_bytes":2148,"put_count":11}} +{"i":70,"series":1,"stats":{"get_bytes":3290,"get_count":6,"put_bytes":2548,"put_count":12}} +{"i":71,"series":1,"stats":{"get_bytes":3690,"get_count":7,"put_bytes":2861,"put_count":12}} +{"i":72,"series":1,"stats":{"get_bytes":3859,"get_count":6,"put_bytes":3117,"put_count":12}} +{"i":73,"series":1,"stats":{"get_bytes":2683,"get_count":6,"put_bytes":1853,"put_count":11}} +{"i":74,"series":1,"stats":{"get_bytes":2995,"get_count":6,"put_bytes":2253,"put_count":12}} +{"i":75,"series":1,"stats":{"get_bytes":3395,"get_count":7,"put_bytes":2565,"put_count":12}} +{"i":76,"series":1,"stats":{"get_bytes":3563,"get_count":6,"put_bytes":2770,"put_count":11}} +{"i":77,"series":1,"stats":{"get_bytes":3912,"get_count":6,"put_bytes":3134,"put_count":12}} +{"i":78,"series":1,"stats":{"get_bytes":2700,"get_count":6,"put_bytes":1959,"put_count":12}} +{"i":79,"series":1,"stats":{"get_bytes":3101,"get_count":7,"put_bytes":2270,"put_count":12}} +{"i":80,"series":1,"stats":{"get_bytes":3268,"get_count":6,"put_bytes":2474,"put_count":11}} +{"i":81,"series":1,"stats":{"get_bytes":3616,"get_count":6,"put_bytes":2787,"put_count":11}} +{"i":82,"series":1,"stats":{"get_bytes":3929,"get_count":6,"put_bytes":3240,"put_count":13}} +{"i":83,"series":1,"stats":{"get_bytes":2806,"get_count":7,"put_bytes":1975,"put_count":12}} +{"i":84,"series":1,"stats":{"get_bytes":2973,"get_count":6,"put_bytes":2179,"put_count":11}} +{"i":85,"series":1,"stats":{"get_bytes":3321,"get_count":6,"put_bytes":2491,"put_count":11}} +{"i":86,"series":1,"stats":{"get_bytes":3633,"get_count":6,"put_bytes":2893,"put_count":12}} +{"i":87,"series":1,"stats":{"get_bytes":4035,"get_count":7,"put_bytes":3205,"put_count":12}} +{"i":88,"series":1,"stats":{"get_bytes":2771,"get_count":6,"put_bytes":1884,"put_count":11}} +{"i":89,"series":1,"stats":{"get_bytes":3026,"get_count":6,"put_bytes":2196,"put_count":11}} +{"i":90,"series":1,"stats":{"get_bytes":3338,"get_count":6,"put_bytes":2597,"put_count":12}} +{"i":91,"series":1,"stats":{"get_bytes":3739,"get_count":7,"put_bytes":2910,"put_count":12}} +{"i":92,"series":1,"stats":{"get_bytes":3908,"get_count":6,"put_bytes":3113,"put_count":11}} +{"i":93,"series":1,"stats":{"get_bytes":2679,"get_count":5,"put_bytes":1901,"put_count":11}} +{"i":94,"series":1,"stats":{"get_bytes":3043,"get_count":6,"put_bytes":2302,"put_count":12}} +{"i":95,"series":1,"stats":{"get_bytes":3444,"get_count":7,"put_bytes":2614,"put_count":12}} +{"i":96,"series":1,"stats":{"get_bytes":3612,"get_count":6,"put_bytes":2819,"put_count":11}} +{"i":97,"series":1,"stats":{"get_bytes":3961,"get_count":6,"put_bytes":3130,"put_count":11}} +{"i":98,"series":1,"stats":{"get_bytes":4272,"get_count":6,"put_bytes":3583,"put_count":13}} +{"i":99,"series":1,"stats":{"get_bytes":3149,"get_count":7,"put_bytes":2319,"put_count":12}} +{"i":0,"series":2,"stats":{"get_bytes":2226,"get_count":3,"put_bytes":1613,"put_count":10}} +{"i":1,"series":2,"stats":{"get_bytes":2755,"get_count":5,"put_bytes":1925,"put_count":10}} +{"i":2,"series":2,"stats":{"get_bytes":3067,"get_count":5,"put_bytes":2327,"put_count":11}} +{"i":3,"series":2,"stats":{"get_bytes":3469,"get_count":6,"put_bytes":2639,"put_count":11}} +{"i":4,"series":2,"stats":{"get_bytes":3637,"get_count":5,"put_bytes":1715,"put_count":11}} +{"i":5,"series":2,"stats":{"get_bytes":2857,"get_count":6,"put_bytes":2027,"put_count":11}} +{"i":6,"series":2,"stats":{"get_bytes":3169,"get_count":6,"put_bytes":2428,"put_count":12}} +{"i":7,"series":2,"stats":{"get_bytes":3570,"get_count":7,"put_bytes":2741,"put_count":12}} +{"i":8,"series":2,"stats":{"get_bytes":3739,"get_count":6,"put_bytes":2944,"put_count":11}} +{"i":9,"series":2,"stats":{"get_bytes":4086,"get_count":6,"put_bytes":3308,"put_count":12}} +{"i":10,"series":2,"stats":{"get_bytes":2874,"get_count":6,"put_bytes":2133,"put_count":12}} +{"i":11,"series":2,"stats":{"get_bytes":3275,"get_count":7,"put_bytes":2445,"put_count":12}} +{"i":12,"series":2,"stats":{"get_bytes":3443,"get_count":6,"put_bytes":2650,"put_count":11}} +{"i":13,"series":2,"stats":{"get_bytes":3792,"get_count":6,"put_bytes":2961,"put_count":11}} +{"i":14,"series":2,"stats":{"get_bytes":4103,"get_count":6,"put_bytes":3414,"put_count":13}} +{"i":15,"series":2,"stats":{"get_bytes":2980,"get_count":7,"put_bytes":2150,"put_count":12}} +{"i":16,"series":2,"stats":{"get_bytes":3148,"get_count":6,"put_bytes":2354,"put_count":11}} +{"i":17,"series":2,"stats":{"get_bytes":3496,"get_count":6,"put_bytes":2666,"put_count":11}} +{"i":18,"series":2,"stats":{"get_bytes":3808,"get_count":6,"put_bytes":3067,"put_count":12}} +{"i":19,"series":2,"stats":{"get_bytes":4209,"get_count":7,"put_bytes":3431,"put_count":13}} +{"i":20,"series":2,"stats":{"get_bytes":2853,"get_count":6,"put_bytes":2059,"put_count":11}} +{"i":21,"series":2,"stats":{"get_bytes":3201,"get_count":6,"put_bytes":2371,"put_count":11}} +{"i":22,"series":2,"stats":{"get_bytes":3513,"get_count":6,"put_bytes":2771,"put_count":12}} +{"i":23,"series":2,"stats":{"get_bytes":3913,"get_count":7,"put_bytes":3084,"put_count":12}} +{"i":24,"series":2,"stats":{"get_bytes":4082,"get_count":6,"put_bytes":3341,"put_count":12}} +{"i":25,"series":2,"stats":{"get_bytes":2907,"get_count":6,"put_bytes":2077,"put_count":11}} +{"i":26,"series":2,"stats":{"get_bytes":3219,"get_count":6,"put_bytes":2477,"put_count":12}} +{"i":27,"series":2,"stats":{"get_bytes":3619,"get_count":7,"put_bytes":2789,"put_count":12}} +{"i":28,"series":2,"stats":{"get_bytes":3787,"get_count":6,"put_bytes":2994,"put_count":11}} +{"i":29,"series":2,"stats":{"get_bytes":4136,"get_count":6,"put_bytes":3358,"put_count":12}} +{"i":30,"series":2,"stats":{"get_bytes":2924,"get_count":6,"put_bytes":2183,"put_count":12}} +{"i":31,"series":2,"stats":{"get_bytes":3325,"get_count":7,"put_bytes":2494,"put_count":12}} +{"i":32,"series":2,"stats":{"get_bytes":3492,"get_count":6,"put_bytes":2698,"put_count":11}} +{"i":33,"series":2,"stats":{"get_bytes":3840,"get_count":6,"put_bytes":3011,"put_count":11}} +{"i":34,"series":2,"stats":{"get_bytes":4153,"get_count":6,"put_bytes":3464,"put_count":13}} +{"i":35,"series":2,"stats":{"get_bytes":3030,"get_count":7,"put_bytes":2199,"put_count":12}} +{"i":36,"series":2,"stats":{"get_bytes":3197,"get_count":6,"put_bytes":2403,"put_count":11}} +{"i":37,"series":2,"stats":{"get_bytes":3545,"get_count":6,"put_bytes":2715,"put_count":11}} +{"i":38,"series":2,"stats":{"get_bytes":3857,"get_count":6,"put_bytes":3117,"put_count":12}} +{"i":39,"series":2,"stats":{"get_bytes":4259,"get_count":7,"put_bytes":3429,"put_count":12}} +{"i":40,"series":2,"stats":{"get_bytes":2995,"get_count":6,"put_bytes":2108,"put_count":11}} +{"i":41,"series":2,"stats":{"get_bytes":3250,"get_count":6,"put_bytes":2420,"put_count":11}} +{"i":42,"series":2,"stats":{"get_bytes":3562,"get_count":6,"put_bytes":2821,"put_count":12}} +{"i":43,"series":2,"stats":{"get_bytes":3963,"get_count":7,"put_bytes":3134,"put_count":12}} +{"i":44,"series":2,"stats":{"get_bytes":4132,"get_count":6,"put_bytes":3337,"put_count":11}} +{"i":45,"series":2,"stats":{"get_bytes":2903,"get_count":5,"put_bytes":2125,"put_count":11}} +{"i":46,"series":2,"stats":{"get_bytes":3267,"get_count":6,"put_bytes":2526,"put_count":12}} +{"i":47,"series":2,"stats":{"get_bytes":3668,"get_count":7,"put_bytes":2838,"put_count":12}} +{"i":48,"series":2,"stats":{"get_bytes":3836,"get_count":6,"put_bytes":3043,"put_count":11}} +{"i":49,"series":2,"stats":{"get_bytes":4185,"get_count":6,"put_bytes":3354,"put_count":11}} +{"i":50,"series":2,"stats":{"get_bytes":4496,"get_count":6,"put_bytes":3807,"put_count":13}} +{"i":51,"series":2,"stats":{"get_bytes":3373,"get_count":7,"put_bytes":2543,"put_count":12}} +{"i":52,"series":2,"stats":{"get_bytes":3541,"get_count":6,"put_bytes":2747,"put_count":11}} +{"i":53,"series":2,"stats":{"get_bytes":3889,"get_count":6,"put_bytes":3060,"put_count":11}} +{"i":54,"series":2,"stats":{"get_bytes":4202,"get_count":6,"put_bytes":3460,"put_count":12}} +{"i":55,"series":2,"stats":{"get_bytes":4602,"get_count":7,"put_bytes":3824,"put_count":13}} +{"i":56,"series":2,"stats":{"get_bytes":3246,"get_count":6,"put_bytes":2452,"put_count":11}} +{"i":57,"series":2,"stats":{"get_bytes":3594,"get_count":6,"put_bytes":2764,"put_count":11}} +{"i":58,"series":2,"stats":{"get_bytes":3906,"get_count":6,"put_bytes":3165,"put_count":12}} +{"i":59,"series":2,"stats":{"get_bytes":4307,"get_count":7,"put_bytes":3477,"put_count":12}} +{"i":60,"series":2,"stats":{"get_bytes":4475,"get_count":6,"put_bytes":3733,"put_count":12}} +{"i":61,"series":2,"stats":{"get_bytes":3299,"get_count":6,"put_bytes":2469,"put_count":11}} +{"i":62,"series":2,"stats":{"get_bytes":3611,"get_count":6,"put_bytes":2870,"put_count":12}} +{"i":63,"series":2,"stats":{"get_bytes":4012,"get_count":7,"put_bytes":3181,"put_count":12}} +{"i":64,"series":2,"stats":{"get_bytes":4179,"get_count":6,"put_bytes":3386,"put_count":11}} +{"i":65,"series":2,"stats":{"get_bytes":4528,"get_count":6,"put_bytes":3751,"put_count":12}} +{"i":66,"series":2,"stats":{"get_bytes":3317,"get_count":6,"put_bytes":2576,"put_count":12}} +{"i":67,"series":2,"stats":{"get_bytes":3718,"get_count":7,"put_bytes":2887,"put_count":12}} +{"i":68,"series":2,"stats":{"get_bytes":3885,"get_count":6,"put_bytes":3091,"put_count":11}} +{"i":69,"series":2,"stats":{"get_bytes":4233,"get_count":6,"put_bytes":3404,"put_count":11}} +{"i":70,"series":2,"stats":{"get_bytes":4546,"get_count":6,"put_bytes":3857,"put_count":13}} +{"i":71,"series":2,"stats":{"get_bytes":3423,"get_count":7,"put_bytes":2593,"put_count":12}} +{"i":72,"series":2,"stats":{"get_bytes":3591,"get_count":6,"put_bytes":2796,"put_count":11}} +{"i":73,"series":2,"stats":{"get_bytes":3938,"get_count":6,"put_bytes":3108,"put_count":11}} +{"i":74,"series":2,"stats":{"get_bytes":4250,"get_count":6,"put_bytes":3510,"put_count":12}} +{"i":75,"series":2,"stats":{"get_bytes":4652,"get_count":7,"put_bytes":3874,"put_count":13}} +{"i":76,"series":2,"stats":{"get_bytes":3296,"get_count":6,"put_bytes":2502,"put_count":11}} +{"i":77,"series":2,"stats":{"get_bytes":3644,"get_count":6,"put_bytes":2813,"put_count":11}} +{"i":78,"series":2,"stats":{"get_bytes":3955,"get_count":6,"put_bytes":3214,"put_count":12}} +{"i":79,"series":2,"stats":{"get_bytes":4356,"get_count":7,"put_bytes":3527,"put_count":12}} +{"i":80,"series":2,"stats":{"get_bytes":4525,"get_count":6,"put_bytes":3783,"put_count":12}} +{"i":81,"series":2,"stats":{"get_bytes":3349,"get_count":6,"put_bytes":2518,"put_count":11}} +{"i":82,"series":2,"stats":{"get_bytes":3660,"get_count":6,"put_bytes":2919,"put_count":12}} +{"i":83,"series":2,"stats":{"get_bytes":4061,"get_count":7,"put_bytes":3231,"put_count":12}} +{"i":84,"series":2,"stats":{"get_bytes":4229,"get_count":6,"put_bytes":3436,"put_count":11}} +{"i":85,"series":2,"stats":{"get_bytes":4578,"get_count":6,"put_bytes":3747,"put_count":11}} +{"i":86,"series":2,"stats":{"get_bytes":3313,"get_count":5,"put_bytes":2624,"put_count":12}} +{"i":87,"series":2,"stats":{"get_bytes":3766,"get_count":7,"put_bytes":2936,"put_count":12}} +{"i":88,"series":2,"stats":{"get_bytes":3934,"get_count":6,"put_bytes":3140,"put_count":11}} +{"i":89,"series":2,"stats":{"get_bytes":4282,"get_count":6,"put_bytes":3453,"put_count":11}} +{"i":90,"series":2,"stats":{"get_bytes":4595,"get_count":6,"put_bytes":3853,"put_count":12}} +{"i":91,"series":2,"stats":{"get_bytes":4995,"get_count":7,"put_bytes":4217,"put_count":13}} +{"i":92,"series":2,"stats":{"get_bytes":3639,"get_count":6,"put_bytes":2845,"put_count":11}} +{"i":93,"series":2,"stats":{"get_bytes":3987,"get_count":6,"put_bytes":3157,"put_count":11}} +{"i":94,"series":2,"stats":{"get_bytes":4299,"get_count":6,"put_bytes":3559,"put_count":12}} +{"i":95,"series":2,"stats":{"get_bytes":4701,"get_count":7,"put_bytes":3870,"put_count":12}} +{"i":96,"series":2,"stats":{"get_bytes":4868,"get_count":6,"put_bytes":4126,"put_count":12}} +{"i":97,"series":2,"stats":{"get_bytes":3692,"get_count":6,"put_bytes":2862,"put_count":11}} +{"i":98,"series":2,"stats":{"get_bytes":4004,"get_count":6,"put_bytes":3263,"put_count":12}} +{"i":99,"series":2,"stats":{"get_bytes":4405,"get_count":7,"put_bytes":3575,"put_count":12}} diff --git a/actors/evm/tests/measurements/array_push_n100.png b/actors/evm/tests/measurements/array_push_n100.png new file mode 100644 index 000000000..f2b9dd5bb Binary files /dev/null and b/actors/evm/tests/measurements/array_push_n100.png differ diff --git a/actors/evm/tests/measurements/array_read_n1.jsonline b/actors/evm/tests/measurements/array_read_n1.jsonline new file mode 100644 index 000000000..97cf21610 --- /dev/null +++ b/actors/evm/tests/measurements/array_read_n1.jsonline @@ -0,0 +1,100 @@ +{"i":0,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":1,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":2,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":3,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":4,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":5,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":6,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":7,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":8,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":9,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":10,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":11,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":12,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":13,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":14,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":15,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":16,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":17,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":18,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":19,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":20,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":21,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":22,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":23,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":24,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":25,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":26,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":27,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":28,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":29,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":30,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":31,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":32,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":33,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":34,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":35,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":36,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":37,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":38,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":39,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":40,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":41,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":42,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":43,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":44,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":45,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":46,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":47,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":48,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":49,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":50,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":51,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":52,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":53,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":54,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":55,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":56,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":57,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":58,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":59,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":60,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":61,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":62,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":63,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":64,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":65,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":66,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":67,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":68,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":69,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":70,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":71,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":72,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":73,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":74,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":75,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":76,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":77,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":78,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":79,"series":1,"stats":{"get_bytes":3368,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":80,"series":1,"stats":{"get_bytes":4692,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":81,"series":1,"stats":{"get_bytes":4692,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":82,"series":1,"stats":{"get_bytes":4692,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":83,"series":1,"stats":{"get_bytes":4692,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":84,"series":1,"stats":{"get_bytes":4692,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":85,"series":1,"stats":{"get_bytes":4692,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":86,"series":1,"stats":{"get_bytes":4692,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":87,"series":1,"stats":{"get_bytes":4692,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":88,"series":1,"stats":{"get_bytes":4692,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":89,"series":1,"stats":{"get_bytes":4692,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":90,"series":1,"stats":{"get_bytes":4692,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":91,"series":1,"stats":{"get_bytes":4692,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":92,"series":1,"stats":{"get_bytes":4692,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":93,"series":1,"stats":{"get_bytes":4692,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":94,"series":1,"stats":{"get_bytes":4692,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":95,"series":1,"stats":{"get_bytes":4692,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":96,"series":1,"stats":{"get_bytes":4692,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":97,"series":1,"stats":{"get_bytes":4692,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":98,"series":1,"stats":{"get_bytes":4692,"get_count":7,"put_bytes":0,"put_count":0}} +{"i":99,"series":1,"stats":{"get_bytes":4692,"get_count":7,"put_bytes":0,"put_count":0}} diff --git a/actors/evm/tests/measurements/array_read_n1.png b/actors/evm/tests/measurements/array_read_n1.png new file mode 100644 index 000000000..ceaa715a5 Binary files /dev/null and b/actors/evm/tests/measurements/array_read_n1.png differ diff --git a/actors/evm/tests/measurements/array_read_n100.jsonline b/actors/evm/tests/measurements/array_read_n100.jsonline new file mode 100644 index 000000000..2a9ce7132 --- /dev/null +++ b/actors/evm/tests/measurements/array_read_n100.jsonline @@ -0,0 +1,100 @@ +{"i":0,"series":1,"stats":{"get_bytes":5808,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":1,"series":1,"stats":{"get_bytes":5556,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":2,"series":1,"stats":{"get_bytes":5563,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":3,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":4,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":5,"series":1,"stats":{"get_bytes":7146,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":6,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":7,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":8,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":9,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":10,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":11,"series":1,"stats":{"get_bytes":7146,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":12,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":13,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":14,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":15,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":16,"series":1,"stats":{"get_bytes":7146,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":17,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":18,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":19,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":20,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":21,"series":1,"stats":{"get_bytes":7146,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":22,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":23,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":24,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":25,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":26,"series":1,"stats":{"get_bytes":7146,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":27,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":28,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":29,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":30,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":31,"series":1,"stats":{"get_bytes":7146,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":32,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":33,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":34,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":35,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":36,"series":1,"stats":{"get_bytes":7146,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":37,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":38,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":39,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":40,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":41,"series":1,"stats":{"get_bytes":7146,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":42,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":43,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":44,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":45,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":46,"series":1,"stats":{"get_bytes":7146,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":47,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":48,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":49,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":50,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":51,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":52,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":53,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":54,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":55,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":56,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":57,"series":1,"stats":{"get_bytes":7146,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":58,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":59,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":60,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":61,"series":1,"stats":{"get_bytes":5570,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":62,"series":1,"stats":{"get_bytes":7542,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":63,"series":1,"stats":{"get_bytes":5322,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":64,"series":1,"stats":{"get_bytes":5322,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":65,"series":1,"stats":{"get_bytes":5322,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":66,"series":1,"stats":{"get_bytes":5322,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":67,"series":1,"stats":{"get_bytes":6898,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":68,"series":1,"stats":{"get_bytes":5322,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":69,"series":1,"stats":{"get_bytes":5322,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":70,"series":1,"stats":{"get_bytes":5322,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":71,"series":1,"stats":{"get_bytes":5322,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":72,"series":1,"stats":{"get_bytes":6898,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":73,"series":1,"stats":{"get_bytes":5322,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":74,"series":1,"stats":{"get_bytes":5322,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":75,"series":1,"stats":{"get_bytes":5322,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":76,"series":1,"stats":{"get_bytes":5322,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":77,"series":1,"stats":{"get_bytes":6898,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":78,"series":1,"stats":{"get_bytes":5322,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":79,"series":1,"stats":{"get_bytes":5322,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":80,"series":1,"stats":{"get_bytes":5322,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":81,"series":1,"stats":{"get_bytes":5322,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":82,"series":1,"stats":{"get_bytes":6898,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":83,"series":1,"stats":{"get_bytes":5322,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":84,"series":1,"stats":{"get_bytes":5322,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":85,"series":1,"stats":{"get_bytes":5322,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":86,"series":1,"stats":{"get_bytes":5322,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":87,"series":1,"stats":{"get_bytes":6898,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":88,"series":1,"stats":{"get_bytes":5322,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":89,"series":1,"stats":{"get_bytes":5322,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":90,"series":1,"stats":{"get_bytes":5322,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":91,"series":1,"stats":{"get_bytes":5322,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":92,"series":1,"stats":{"get_bytes":6898,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":93,"series":1,"stats":{"get_bytes":5322,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":94,"series":1,"stats":{"get_bytes":5322,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":95,"series":1,"stats":{"get_bytes":5322,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":96,"series":1,"stats":{"get_bytes":5322,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":97,"series":1,"stats":{"get_bytes":5322,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":98,"series":1,"stats":{"get_bytes":5915,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":99,"series":1,"stats":{"get_bytes":4339,"get_count":13,"put_bytes":0,"put_count":0}} diff --git a/actors/evm/tests/measurements/array_read_n100.png b/actors/evm/tests/measurements/array_read_n100.png new file mode 100644 index 000000000..23c0d0026 Binary files /dev/null and b/actors/evm/tests/measurements/array_read_n100.png differ diff --git a/actors/evm/tests/measurements/inc_after_fill.jsonline b/actors/evm/tests/measurements/inc_after_fill.jsonline new file mode 100644 index 000000000..703a6aa6e --- /dev/null +++ b/actors/evm/tests/measurements/inc_after_fill.jsonline @@ -0,0 +1,200 @@ +{"i":0,"series":1,"stats":{"get_bytes":2227,"get_count":3,"put_bytes":316,"put_count":3}} +{"i":1,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":2,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":3,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":4,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":5,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":6,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":7,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":8,"series":1,"stats":{"get_bytes":2320,"get_count":4,"put_bytes":314,"put_count":3}} +{"i":9,"series":1,"stats":{"get_bytes":2320,"get_count":4,"put_bytes":314,"put_count":3}} +{"i":10,"series":1,"stats":{"get_bytes":2320,"get_count":4,"put_bytes":314,"put_count":3}} +{"i":11,"series":1,"stats":{"get_bytes":2320,"get_count":4,"put_bytes":314,"put_count":3}} +{"i":12,"series":1,"stats":{"get_bytes":2320,"get_count":4,"put_bytes":314,"put_count":3}} +{"i":13,"series":1,"stats":{"get_bytes":2320,"get_count":4,"put_bytes":314,"put_count":3}} +{"i":14,"series":1,"stats":{"get_bytes":2320,"get_count":4,"put_bytes":314,"put_count":3}} +{"i":15,"series":1,"stats":{"get_bytes":2320,"get_count":4,"put_bytes":314,"put_count":3}} +{"i":16,"series":1,"stats":{"get_bytes":2320,"get_count":4,"put_bytes":314,"put_count":3}} +{"i":17,"series":1,"stats":{"get_bytes":2320,"get_count":4,"put_bytes":314,"put_count":3}} +{"i":18,"series":1,"stats":{"get_bytes":2320,"get_count":4,"put_bytes":314,"put_count":3}} +{"i":19,"series":1,"stats":{"get_bytes":2320,"get_count":4,"put_bytes":314,"put_count":3}} +{"i":20,"series":1,"stats":{"get_bytes":2320,"get_count":4,"put_bytes":314,"put_count":3}} +{"i":21,"series":1,"stats":{"get_bytes":2320,"get_count":4,"put_bytes":314,"put_count":3}} +{"i":22,"series":1,"stats":{"get_bytes":2320,"get_count":4,"put_bytes":314,"put_count":3}} +{"i":23,"series":1,"stats":{"get_bytes":2320,"get_count":4,"put_bytes":314,"put_count":3}} +{"i":24,"series":1,"stats":{"get_bytes":2320,"get_count":4,"put_bytes":314,"put_count":3}} +{"i":25,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":26,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":27,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":28,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":29,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":30,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":31,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":32,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":33,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":34,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":35,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":36,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":37,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":38,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":39,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":40,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":41,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":42,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":43,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":44,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":45,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":46,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":47,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":48,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":49,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":50,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":51,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":52,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":53,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":54,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":55,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":56,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":57,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":58,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":59,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":60,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":61,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":62,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":63,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":64,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":65,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":66,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":67,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":68,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":69,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":70,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":71,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":72,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":73,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":74,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":75,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":76,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":77,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":78,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":79,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":80,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":81,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":82,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":83,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":84,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":85,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":86,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":87,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":88,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":89,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":90,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":91,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":92,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":93,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":94,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":95,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":96,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":97,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":98,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":99,"series":1,"stats":{"get_bytes":2321,"get_count":4,"put_bytes":315,"put_count":3}} +{"i":0,"series":2,"stats":{"get_bytes":2480,"get_count":3,"put_bytes":482,"put_count":2}} +{"i":1,"series":2,"stats":{"get_bytes":2767,"get_count":3,"put_bytes":761,"put_count":2}} +{"i":2,"series":2,"stats":{"get_bytes":2979,"get_count":3,"put_bytes":973,"put_count":2}} +{"i":3,"series":2,"stats":{"get_bytes":3210,"get_count":3,"put_bytes":1204,"put_count":2}} +{"i":4,"series":2,"stats":{"get_bytes":3307,"get_count":3,"put_bytes":1301,"put_count":2}} +{"i":5,"series":2,"stats":{"get_bytes":3479,"get_count":3,"put_bytes":1473,"put_count":2}} +{"i":6,"series":2,"stats":{"get_bytes":3613,"get_count":4,"put_bytes":1607,"put_count":3}} +{"i":7,"series":2,"stats":{"get_bytes":3678,"get_count":4,"put_bytes":1672,"put_count":3}} +{"i":8,"series":2,"stats":{"get_bytes":3710,"get_count":4,"put_bytes":1704,"put_count":3}} +{"i":9,"series":2,"stats":{"get_bytes":3726,"get_count":4,"put_bytes":1720,"put_count":3}} +{"i":10,"series":2,"stats":{"get_bytes":3742,"get_count":4,"put_bytes":1736,"put_count":3}} +{"i":11,"series":2,"stats":{"get_bytes":3782,"get_count":4,"put_bytes":1776,"put_count":3}} +{"i":12,"series":2,"stats":{"get_bytes":3782,"get_count":4,"put_bytes":1776,"put_count":3}} +{"i":13,"series":2,"stats":{"get_bytes":3782,"get_count":4,"put_bytes":1776,"put_count":3}} +{"i":14,"series":2,"stats":{"get_bytes":3790,"get_count":4,"put_bytes":1784,"put_count":3}} +{"i":15,"series":2,"stats":{"get_bytes":3790,"get_count":4,"put_bytes":1784,"put_count":3}} +{"i":16,"series":2,"stats":{"get_bytes":3883,"get_count":4,"put_bytes":1877,"put_count":3}} +{"i":17,"series":2,"stats":{"get_bytes":3883,"get_count":4,"put_bytes":1877,"put_count":3}} +{"i":18,"series":2,"stats":{"get_bytes":3883,"get_count":4,"put_bytes":1877,"put_count":3}} +{"i":19,"series":2,"stats":{"get_bytes":3924,"get_count":4,"put_bytes":1918,"put_count":3}} +{"i":20,"series":2,"stats":{"get_bytes":3924,"get_count":4,"put_bytes":1918,"put_count":3}} +{"i":21,"series":2,"stats":{"get_bytes":3924,"get_count":4,"put_bytes":1918,"put_count":3}} +{"i":22,"series":2,"stats":{"get_bytes":3924,"get_count":4,"put_bytes":1918,"put_count":3}} +{"i":23,"series":2,"stats":{"get_bytes":3924,"get_count":4,"put_bytes":1918,"put_count":3}} +{"i":24,"series":2,"stats":{"get_bytes":3924,"get_count":4,"put_bytes":1918,"put_count":3}} +{"i":25,"series":2,"stats":{"get_bytes":3924,"get_count":4,"put_bytes":1918,"put_count":3}} +{"i":26,"series":2,"stats":{"get_bytes":3924,"get_count":4,"put_bytes":1918,"put_count":3}} +{"i":27,"series":2,"stats":{"get_bytes":3924,"get_count":4,"put_bytes":1918,"put_count":3}} +{"i":28,"series":2,"stats":{"get_bytes":3924,"get_count":4,"put_bytes":1918,"put_count":3}} +{"i":29,"series":2,"stats":{"get_bytes":3924,"get_count":4,"put_bytes":1918,"put_count":3}} +{"i":30,"series":2,"stats":{"get_bytes":3965,"get_count":4,"put_bytes":1959,"put_count":3}} +{"i":31,"series":2,"stats":{"get_bytes":3965,"get_count":4,"put_bytes":1959,"put_count":3}} +{"i":32,"series":2,"stats":{"get_bytes":3965,"get_count":4,"put_bytes":1959,"put_count":3}} +{"i":33,"series":2,"stats":{"get_bytes":3965,"get_count":4,"put_bytes":1959,"put_count":3}} +{"i":34,"series":2,"stats":{"get_bytes":3965,"get_count":4,"put_bytes":1959,"put_count":3}} +{"i":35,"series":2,"stats":{"get_bytes":3965,"get_count":4,"put_bytes":1959,"put_count":3}} +{"i":36,"series":2,"stats":{"get_bytes":3965,"get_count":4,"put_bytes":1959,"put_count":3}} +{"i":37,"series":2,"stats":{"get_bytes":3965,"get_count":4,"put_bytes":1959,"put_count":3}} +{"i":38,"series":2,"stats":{"get_bytes":3965,"get_count":4,"put_bytes":1959,"put_count":3}} +{"i":39,"series":2,"stats":{"get_bytes":3965,"get_count":4,"put_bytes":1959,"put_count":3}} +{"i":40,"series":2,"stats":{"get_bytes":3965,"get_count":4,"put_bytes":1959,"put_count":3}} +{"i":41,"series":2,"stats":{"get_bytes":3965,"get_count":4,"put_bytes":1959,"put_count":3}} +{"i":42,"series":2,"stats":{"get_bytes":3965,"get_count":4,"put_bytes":1959,"put_count":3}} +{"i":43,"series":2,"stats":{"get_bytes":3965,"get_count":4,"put_bytes":1959,"put_count":3}} +{"i":44,"series":2,"stats":{"get_bytes":3965,"get_count":4,"put_bytes":1959,"put_count":3}} +{"i":45,"series":2,"stats":{"get_bytes":3965,"get_count":4,"put_bytes":1959,"put_count":3}} +{"i":46,"series":2,"stats":{"get_bytes":4006,"get_count":4,"put_bytes":2000,"put_count":3}} +{"i":47,"series":2,"stats":{"get_bytes":4006,"get_count":4,"put_bytes":2000,"put_count":3}} +{"i":48,"series":2,"stats":{"get_bytes":4006,"get_count":4,"put_bytes":2000,"put_count":3}} +{"i":49,"series":2,"stats":{"get_bytes":4006,"get_count":4,"put_bytes":2000,"put_count":3}} +{"i":50,"series":2,"stats":{"get_bytes":4006,"get_count":4,"put_bytes":2000,"put_count":3}} +{"i":51,"series":2,"stats":{"get_bytes":4006,"get_count":4,"put_bytes":2000,"put_count":3}} +{"i":52,"series":2,"stats":{"get_bytes":4006,"get_count":4,"put_bytes":2000,"put_count":3}} +{"i":53,"series":2,"stats":{"get_bytes":4006,"get_count":4,"put_bytes":2000,"put_count":3}} +{"i":54,"series":2,"stats":{"get_bytes":4006,"get_count":4,"put_bytes":2000,"put_count":3}} +{"i":55,"series":2,"stats":{"get_bytes":4006,"get_count":4,"put_bytes":2000,"put_count":3}} +{"i":56,"series":2,"stats":{"get_bytes":4047,"get_count":4,"put_bytes":2041,"put_count":3}} +{"i":57,"series":2,"stats":{"get_bytes":4088,"get_count":4,"put_bytes":2082,"put_count":3}} +{"i":58,"series":2,"stats":{"get_bytes":4088,"get_count":4,"put_bytes":2082,"put_count":3}} +{"i":59,"series":2,"stats":{"get_bytes":4088,"get_count":4,"put_bytes":2082,"put_count":3}} +{"i":60,"series":2,"stats":{"get_bytes":4088,"get_count":4,"put_bytes":2082,"put_count":3}} +{"i":61,"series":2,"stats":{"get_bytes":4129,"get_count":4,"put_bytes":2123,"put_count":3}} +{"i":62,"series":2,"stats":{"get_bytes":4129,"get_count":4,"put_bytes":2123,"put_count":3}} +{"i":63,"series":2,"stats":{"get_bytes":4129,"get_count":4,"put_bytes":2123,"put_count":3}} +{"i":64,"series":2,"stats":{"get_bytes":4129,"get_count":4,"put_bytes":2123,"put_count":3}} +{"i":65,"series":2,"stats":{"get_bytes":4129,"get_count":4,"put_bytes":2123,"put_count":3}} +{"i":66,"series":2,"stats":{"get_bytes":4129,"get_count":4,"put_bytes":2123,"put_count":3}} +{"i":67,"series":2,"stats":{"get_bytes":4129,"get_count":4,"put_bytes":2123,"put_count":3}} +{"i":68,"series":2,"stats":{"get_bytes":4170,"get_count":4,"put_bytes":2164,"put_count":3}} +{"i":69,"series":2,"stats":{"get_bytes":4219,"get_count":4,"put_bytes":2213,"put_count":3}} +{"i":70,"series":2,"stats":{"get_bytes":4219,"get_count":4,"put_bytes":2213,"put_count":3}} +{"i":71,"series":2,"stats":{"get_bytes":4219,"get_count":4,"put_bytes":2213,"put_count":3}} +{"i":72,"series":2,"stats":{"get_bytes":4219,"get_count":4,"put_bytes":2213,"put_count":3}} +{"i":73,"series":2,"stats":{"get_bytes":4219,"get_count":4,"put_bytes":2213,"put_count":3}} +{"i":74,"series":2,"stats":{"get_bytes":4227,"get_count":4,"put_bytes":2221,"put_count":3}} +{"i":75,"series":2,"stats":{"get_bytes":4268,"get_count":4,"put_bytes":2262,"put_count":3}} +{"i":76,"series":2,"stats":{"get_bytes":4268,"get_count":4,"put_bytes":2262,"put_count":3}} +{"i":77,"series":2,"stats":{"get_bytes":4268,"get_count":4,"put_bytes":2262,"put_count":3}} +{"i":78,"series":2,"stats":{"get_bytes":4276,"get_count":4,"put_bytes":2270,"put_count":3}} +{"i":79,"series":2,"stats":{"get_bytes":4276,"get_count":4,"put_bytes":2270,"put_count":3}} +{"i":80,"series":2,"stats":{"get_bytes":4369,"get_count":5,"put_bytes":2363,"put_count":4}} +{"i":81,"series":2,"stats":{"get_bytes":4369,"get_count":5,"put_bytes":2363,"put_count":4}} +{"i":82,"series":2,"stats":{"get_bytes":4410,"get_count":5,"put_bytes":2404,"put_count":4}} +{"i":83,"series":2,"stats":{"get_bytes":4410,"get_count":5,"put_bytes":2404,"put_count":4}} +{"i":84,"series":2,"stats":{"get_bytes":4410,"get_count":5,"put_bytes":2404,"put_count":4}} +{"i":85,"series":2,"stats":{"get_bytes":4410,"get_count":5,"put_bytes":2404,"put_count":4}} +{"i":86,"series":2,"stats":{"get_bytes":4410,"get_count":5,"put_bytes":2404,"put_count":4}} +{"i":87,"series":2,"stats":{"get_bytes":4451,"get_count":5,"put_bytes":2445,"put_count":4}} +{"i":88,"series":2,"stats":{"get_bytes":4451,"get_count":5,"put_bytes":2445,"put_count":4}} +{"i":89,"series":2,"stats":{"get_bytes":4451,"get_count":5,"put_bytes":2445,"put_count":4}} +{"i":90,"series":2,"stats":{"get_bytes":4451,"get_count":5,"put_bytes":2445,"put_count":4}} +{"i":91,"series":2,"stats":{"get_bytes":4492,"get_count":5,"put_bytes":2486,"put_count":4}} +{"i":92,"series":2,"stats":{"get_bytes":4492,"get_count":5,"put_bytes":2486,"put_count":4}} +{"i":93,"series":2,"stats":{"get_bytes":4492,"get_count":5,"put_bytes":2486,"put_count":4}} +{"i":94,"series":2,"stats":{"get_bytes":4492,"get_count":5,"put_bytes":2486,"put_count":4}} +{"i":95,"series":2,"stats":{"get_bytes":4492,"get_count":5,"put_bytes":2486,"put_count":4}} +{"i":96,"series":2,"stats":{"get_bytes":4532,"get_count":5,"put_bytes":2526,"put_count":4}} +{"i":97,"series":2,"stats":{"get_bytes":4532,"get_count":5,"put_bytes":2526,"put_count":4}} +{"i":98,"series":2,"stats":{"get_bytes":4580,"get_count":5,"put_bytes":2574,"put_count":4}} +{"i":99,"series":2,"stats":{"get_bytes":4588,"get_count":5,"put_bytes":2582,"put_count":4}} diff --git a/actors/evm/tests/measurements/inc_after_fill.png b/actors/evm/tests/measurements/inc_after_fill.png new file mode 100644 index 000000000..ad6267388 Binary files /dev/null and b/actors/evm/tests/measurements/inc_after_fill.png differ diff --git a/actors/evm/tests/measurements/inc_one_vs_all.jsonline b/actors/evm/tests/measurements/inc_one_vs_all.jsonline new file mode 100644 index 000000000..1bf5929c9 --- /dev/null +++ b/actors/evm/tests/measurements/inc_one_vs_all.jsonline @@ -0,0 +1,20 @@ +{"i":0,"series":1,"stats":{"get_bytes":2132,"get_count":3,"put_bytes":2267,"put_count":5}} +{"i":0,"series":2,"stats":{"get_bytes":2141,"get_count":3,"put_bytes":147,"put_count":2}} +{"i":1,"series":1,"stats":{"get_bytes":2153,"get_count":3,"put_bytes":147,"put_count":2}} +{"i":1,"series":2,"stats":{"get_bytes":2153,"get_count":3,"put_bytes":147,"put_count":2}} +{"i":2,"series":1,"stats":{"get_bytes":2153,"get_count":3,"put_bytes":147,"put_count":2}} +{"i":2,"series":2,"stats":{"get_bytes":2153,"get_count":3,"put_bytes":147,"put_count":2}} +{"i":3,"series":1,"stats":{"get_bytes":2153,"get_count":3,"put_bytes":147,"put_count":2}} +{"i":3,"series":2,"stats":{"get_bytes":2153,"get_count":3,"put_bytes":147,"put_count":2}} +{"i":4,"series":1,"stats":{"get_bytes":2153,"get_count":3,"put_bytes":147,"put_count":2}} +{"i":4,"series":2,"stats":{"get_bytes":2153,"get_count":3,"put_bytes":147,"put_count":2}} +{"i":5,"series":1,"stats":{"get_bytes":2153,"get_count":3,"put_bytes":147,"put_count":2}} +{"i":5,"series":2,"stats":{"get_bytes":2153,"get_count":3,"put_bytes":147,"put_count":2}} +{"i":6,"series":1,"stats":{"get_bytes":2153,"get_count":3,"put_bytes":147,"put_count":2}} +{"i":6,"series":2,"stats":{"get_bytes":2153,"get_count":3,"put_bytes":147,"put_count":2}} +{"i":7,"series":1,"stats":{"get_bytes":2153,"get_count":3,"put_bytes":147,"put_count":2}} +{"i":7,"series":2,"stats":{"get_bytes":2153,"get_count":3,"put_bytes":147,"put_count":2}} +{"i":8,"series":1,"stats":{"get_bytes":2153,"get_count":3,"put_bytes":147,"put_count":2}} +{"i":8,"series":2,"stats":{"get_bytes":2153,"get_count":3,"put_bytes":147,"put_count":2}} +{"i":9,"series":1,"stats":{"get_bytes":2153,"get_count":3,"put_bytes":147,"put_count":2}} +{"i":9,"series":2,"stats":{"get_bytes":2153,"get_count":3,"put_bytes":147,"put_count":2}} diff --git a/actors/evm/tests/measurements/inc_one_vs_all.png b/actors/evm/tests/measurements/inc_one_vs_all.png new file mode 100644 index 000000000..b845f568a Binary files /dev/null and b/actors/evm/tests/measurements/inc_one_vs_all.png differ diff --git a/actors/evm/tests/measurements/mapping_add_n1.jsonline b/actors/evm/tests/measurements/mapping_add_n1.jsonline new file mode 100644 index 000000000..9e015b085 --- /dev/null +++ b/actors/evm/tests/measurements/mapping_add_n1.jsonline @@ -0,0 +1,200 @@ +{"i":0,"series":1,"stats":{"get_bytes":2132,"get_count":3,"put_bytes":2132,"put_count":3}} +{"i":1,"series":1,"stats":{"get_bytes":2132,"get_count":3,"put_bytes":170,"put_count":2}} +{"i":2,"series":1,"stats":{"get_bytes":2176,"get_count":3,"put_bytes":212,"put_count":2}} +{"i":3,"series":1,"stats":{"get_bytes":2218,"get_count":3,"put_bytes":253,"put_count":2}} +{"i":4,"series":1,"stats":{"get_bytes":2259,"get_count":3,"put_bytes":294,"put_count":2}} +{"i":5,"series":1,"stats":{"get_bytes":2300,"get_count":3,"put_bytes":335,"put_count":2}} +{"i":6,"series":1,"stats":{"get_bytes":2341,"get_count":3,"put_bytes":432,"put_count":3}} +{"i":7,"series":1,"stats":{"get_bytes":2349,"get_count":3,"put_bytes":384,"put_count":2}} +{"i":8,"series":1,"stats":{"get_bytes":2390,"get_count":3,"put_bytes":480,"put_count":3}} +{"i":9,"series":1,"stats":{"get_bytes":2398,"get_count":3,"put_bytes":433,"put_count":2}} +{"i":10,"series":1,"stats":{"get_bytes":2439,"get_count":3,"put_bytes":474,"put_count":2}} +{"i":11,"series":1,"stats":{"get_bytes":2480,"get_count":3,"put_bytes":515,"put_count":2}} +{"i":12,"series":1,"stats":{"get_bytes":2521,"get_count":3,"put_bytes":556,"put_count":2}} +{"i":13,"series":1,"stats":{"get_bytes":2562,"get_count":3,"put_bytes":597,"put_count":2}} +{"i":14,"series":1,"stats":{"get_bytes":2603,"get_count":3,"put_bytes":694,"put_count":3}} +{"i":15,"series":1,"stats":{"get_bytes":2611,"get_count":3,"put_bytes":702,"put_count":3}} +{"i":16,"series":1,"stats":{"get_bytes":2619,"get_count":3,"put_bytes":710,"put_count":3}} +{"i":17,"series":1,"stats":{"get_bytes":2627,"get_count":3,"put_bytes":662,"put_count":2}} +{"i":18,"series":1,"stats":{"get_bytes":2668,"get_count":3,"put_bytes":703,"put_count":2}} +{"i":19,"series":1,"stats":{"get_bytes":2709,"get_count":3,"put_bytes":798,"put_count":3}} +{"i":20,"series":1,"stats":{"get_bytes":2718,"get_count":3,"put_bytes":753,"put_count":2}} +{"i":21,"series":1,"stats":{"get_bytes":2759,"get_count":3,"put_bytes":794,"put_count":2}} +{"i":22,"series":1,"stats":{"get_bytes":2800,"get_count":3,"put_bytes":835,"put_count":2}} +{"i":23,"series":1,"stats":{"get_bytes":2841,"get_count":3,"put_bytes":932,"put_count":3}} +{"i":24,"series":1,"stats":{"get_bytes":2849,"get_count":3,"put_bytes":938,"put_count":3}} +{"i":25,"series":1,"stats":{"get_bytes":2857,"get_count":3,"put_bytes":947,"put_count":3}} +{"i":26,"series":1,"stats":{"get_bytes":2865,"get_count":3,"put_bytes":954,"put_count":3}} +{"i":27,"series":1,"stats":{"get_bytes":2873,"get_count":3,"put_bytes":963,"put_count":3}} +{"i":28,"series":1,"stats":{"get_bytes":2881,"get_count":3,"put_bytes":916,"put_count":2}} +{"i":29,"series":1,"stats":{"get_bytes":2922,"get_count":3,"put_bytes":957,"put_count":2}} +{"i":30,"series":1,"stats":{"get_bytes":2963,"get_count":3,"put_bytes":998,"put_count":2}} +{"i":31,"series":1,"stats":{"get_bytes":3004,"get_count":3,"put_bytes":1039,"put_count":2}} +{"i":32,"series":1,"stats":{"get_bytes":3045,"get_count":3,"put_bytes":1080,"put_count":2}} +{"i":33,"series":1,"stats":{"get_bytes":3086,"get_count":3,"put_bytes":1121,"put_count":2}} +{"i":34,"series":1,"stats":{"get_bytes":3127,"get_count":3,"put_bytes":1218,"put_count":3}} +{"i":35,"series":1,"stats":{"get_bytes":3135,"get_count":3,"put_bytes":1226,"put_count":3}} +{"i":36,"series":1,"stats":{"get_bytes":3143,"get_count":3,"put_bytes":1178,"put_count":2}} +{"i":37,"series":1,"stats":{"get_bytes":3184,"get_count":3,"put_bytes":1275,"put_count":3}} +{"i":38,"series":1,"stats":{"get_bytes":3279,"get_count":4,"put_bytes":1316,"put_count":3}} +{"i":39,"series":1,"stats":{"get_bytes":3192,"get_count":3,"put_bytes":1284,"put_count":3}} +{"i":40,"series":1,"stats":{"get_bytes":3290,"get_count":4,"put_bytes":1325,"put_count":3}} +{"i":41,"series":1,"stats":{"get_bytes":3288,"get_count":4,"put_bytes":1325,"put_count":3}} +{"i":42,"series":1,"stats":{"get_bytes":3201,"get_count":3,"put_bytes":1291,"put_count":3}} +{"i":43,"series":1,"stats":{"get_bytes":3298,"get_count":4,"put_bytes":1333,"put_count":3}} +{"i":44,"series":1,"stats":{"get_bytes":3209,"get_count":3,"put_bytes":1245,"put_count":2}} +{"i":45,"series":1,"stats":{"get_bytes":3381,"get_count":4,"put_bytes":1416,"put_count":3}} +{"i":46,"series":1,"stats":{"get_bytes":3422,"get_count":4,"put_bytes":1457,"put_count":3}} +{"i":47,"series":1,"stats":{"get_bytes":3337,"get_count":4,"put_bytes":1339,"put_count":3}} +{"i":48,"series":1,"stats":{"get_bytes":3250,"get_count":3,"put_bytes":1285,"put_count":2}} +{"i":49,"series":1,"stats":{"get_bytes":3291,"get_count":3,"put_bytes":1381,"put_count":3}} +{"i":50,"series":1,"stats":{"get_bytes":3299,"get_count":3,"put_bytes":1334,"put_count":2}} +{"i":51,"series":1,"stats":{"get_bytes":3429,"get_count":4,"put_bytes":1464,"put_count":3}} +{"i":52,"series":1,"stats":{"get_bytes":3340,"get_count":3,"put_bytes":1375,"put_count":2}} +{"i":53,"series":1,"stats":{"get_bytes":3511,"get_count":4,"put_bytes":1546,"put_count":3}} +{"i":54,"series":1,"stats":{"get_bytes":3381,"get_count":3,"put_bytes":1471,"put_count":3}} +{"i":55,"series":1,"stats":{"get_bytes":3477,"get_count":4,"put_bytes":1512,"put_count":3}} +{"i":56,"series":1,"stats":{"get_bytes":3389,"get_count":3,"put_bytes":1424,"put_count":2}} +{"i":57,"series":1,"stats":{"get_bytes":3430,"get_count":3,"put_bytes":1465,"put_count":2}} +{"i":58,"series":1,"stats":{"get_bytes":3560,"get_count":4,"put_bytes":1595,"put_count":3}} +{"i":59,"series":1,"stats":{"get_bytes":3560,"get_count":4,"put_bytes":1595,"put_count":3}} +{"i":60,"series":1,"stats":{"get_bytes":3471,"get_count":3,"put_bytes":1506,"put_count":2}} +{"i":61,"series":1,"stats":{"get_bytes":3724,"get_count":4,"put_bytes":1815,"put_count":4}} +{"i":62,"series":1,"stats":{"get_bytes":3512,"get_count":3,"put_bytes":1547,"put_count":2}} +{"i":63,"series":1,"stats":{"get_bytes":3642,"get_count":4,"put_bytes":1643,"put_count":3}} +{"i":64,"series":1,"stats":{"get_bytes":3649,"get_count":4,"put_bytes":1684,"put_count":3}} +{"i":65,"series":1,"stats":{"get_bytes":3641,"get_count":4,"put_bytes":1676,"put_count":3}} +{"i":66,"series":1,"stats":{"get_bytes":3640,"get_count":4,"put_bytes":1675,"put_count":3}} +{"i":67,"series":1,"stats":{"get_bytes":3723,"get_count":4,"put_bytes":1758,"put_count":3}} +{"i":68,"series":1,"stats":{"get_bytes":3641,"get_count":4,"put_bytes":1676,"put_count":3}} +{"i":69,"series":1,"stats":{"get_bytes":3647,"get_count":4,"put_bytes":1682,"put_count":3}} +{"i":70,"series":1,"stats":{"get_bytes":3552,"get_count":3,"put_bytes":1642,"put_count":3}} +{"i":71,"series":1,"stats":{"get_bytes":3560,"get_count":3,"put_bytes":1651,"put_count":3}} +{"i":72,"series":1,"stats":{"get_bytes":3656,"get_count":4,"put_bytes":1691,"put_count":3}} +{"i":73,"series":1,"stats":{"get_bytes":3568,"get_count":3,"put_bytes":1603,"put_count":2}} +{"i":74,"series":1,"stats":{"get_bytes":3739,"get_count":4,"put_bytes":1774,"put_count":3}} +{"i":75,"series":1,"stats":{"get_bytes":3609,"get_count":3,"put_bytes":1700,"put_count":3}} +{"i":76,"series":1,"stats":{"get_bytes":3705,"get_count":4,"put_bytes":1740,"put_count":3}} +{"i":77,"series":1,"stats":{"get_bytes":3747,"get_count":4,"put_bytes":1782,"put_count":3}} +{"i":78,"series":1,"stats":{"get_bytes":3747,"get_count":4,"put_bytes":1782,"put_count":3}} +{"i":79,"series":1,"stats":{"get_bytes":3747,"get_count":4,"put_bytes":1782,"put_count":3}} +{"i":80,"series":1,"stats":{"get_bytes":3617,"get_count":3,"put_bytes":1708,"put_count":3}} +{"i":81,"series":1,"stats":{"get_bytes":3714,"get_count":4,"put_bytes":1749,"put_count":3}} +{"i":82,"series":1,"stats":{"get_bytes":3625,"get_count":3,"put_bytes":1714,"put_count":3}} +{"i":83,"series":1,"stats":{"get_bytes":3633,"get_count":3,"put_bytes":1724,"put_count":3}} +{"i":84,"series":1,"stats":{"get_bytes":3770,"get_count":4,"put_bytes":1805,"put_count":3}} +{"i":85,"series":1,"stats":{"get_bytes":3728,"get_count":4,"put_bytes":1763,"put_count":3}} +{"i":86,"series":1,"stats":{"get_bytes":3771,"get_count":4,"put_bytes":1859,"put_count":4}} +{"i":87,"series":1,"stats":{"get_bytes":3861,"get_count":4,"put_bytes":1952,"put_count":4}} +{"i":88,"series":1,"stats":{"get_bytes":3641,"get_count":3,"put_bytes":1730,"put_count":3}} +{"i":89,"series":1,"stats":{"get_bytes":3777,"get_count":4,"put_bytes":1814,"put_count":3}} +{"i":90,"series":1,"stats":{"get_bytes":3820,"get_count":4,"put_bytes":1855,"put_count":3}} +{"i":91,"series":1,"stats":{"get_bytes":3779,"get_count":4,"put_bytes":1814,"put_count":3}} +{"i":92,"series":1,"stats":{"get_bytes":3778,"get_count":4,"put_bytes":1813,"put_count":3}} +{"i":93,"series":1,"stats":{"get_bytes":3820,"get_count":4,"put_bytes":1855,"put_count":3}} +{"i":94,"series":1,"stats":{"get_bytes":3820,"get_count":4,"put_bytes":1855,"put_count":3}} +{"i":95,"series":1,"stats":{"get_bytes":3738,"get_count":4,"put_bytes":1773,"put_count":3}} +{"i":96,"series":1,"stats":{"get_bytes":3861,"get_count":4,"put_bytes":1896,"put_count":3}} +{"i":97,"series":1,"stats":{"get_bytes":3649,"get_count":3,"put_bytes":1740,"put_count":3}} +{"i":98,"series":1,"stats":{"get_bytes":3745,"get_count":4,"put_bytes":1780,"put_count":3}} +{"i":99,"series":1,"stats":{"get_bytes":3657,"get_count":3,"put_bytes":1745,"put_count":3}} +{"i":0,"series":2,"stats":{"get_bytes":3803,"get_count":4,"put_bytes":0,"put_count":0}} +{"i":1,"series":2,"stats":{"get_bytes":3753,"get_count":4,"put_bytes":1788,"put_count":3}} +{"i":2,"series":2,"stats":{"get_bytes":3794,"get_count":4,"put_bytes":1829,"put_count":3}} +{"i":3,"series":2,"stats":{"get_bytes":3794,"get_count":4,"put_bytes":1830,"put_count":3}} +{"i":4,"series":2,"stats":{"get_bytes":3753,"get_count":4,"put_bytes":1788,"put_count":3}} +{"i":5,"series":2,"stats":{"get_bytes":3665,"get_count":3,"put_bytes":1755,"put_count":3}} +{"i":6,"series":2,"stats":{"get_bytes":3811,"get_count":4,"put_bytes":1846,"put_count":3}} +{"i":7,"series":2,"stats":{"get_bytes":3803,"get_count":4,"put_bytes":1838,"put_count":3}} +{"i":8,"series":2,"stats":{"get_bytes":3885,"get_count":4,"put_bytes":1973,"put_count":4}} +{"i":9,"series":2,"stats":{"get_bytes":3803,"get_count":4,"put_bytes":1838,"put_count":3}} +{"i":10,"series":2,"stats":{"get_bytes":3762,"get_count":4,"put_bytes":1797,"put_count":3}} +{"i":11,"series":2,"stats":{"get_bytes":3885,"get_count":4,"put_bytes":1920,"put_count":3}} +{"i":12,"series":2,"stats":{"get_bytes":3811,"get_count":4,"put_bytes":1846,"put_count":3}} +{"i":13,"series":2,"stats":{"get_bytes":3901,"get_count":4,"put_bytes":1936,"put_count":3}} +{"i":14,"series":2,"stats":{"get_bytes":3762,"get_count":4,"put_bytes":1797,"put_count":3}} +{"i":15,"series":2,"stats":{"get_bytes":3759,"get_count":4,"put_bytes":1797,"put_count":3}} +{"i":16,"series":2,"stats":{"get_bytes":3844,"get_count":4,"put_bytes":1879,"put_count":3}} +{"i":17,"series":2,"stats":{"get_bytes":3802,"get_count":4,"put_bytes":1837,"put_count":3}} +{"i":18,"series":2,"stats":{"get_bytes":3673,"get_count":3,"put_bytes":1764,"put_count":3}} +{"i":19,"series":2,"stats":{"get_bytes":3811,"get_count":4,"put_bytes":1846,"put_count":3}} +{"i":20,"series":2,"stats":{"get_bytes":3852,"get_count":4,"put_bytes":1887,"put_count":3}} +{"i":21,"series":2,"stats":{"get_bytes":3810,"get_count":4,"put_bytes":1845,"put_count":3}} +{"i":22,"series":2,"stats":{"get_bytes":3851,"get_count":4,"put_bytes":1887,"put_count":3}} +{"i":23,"series":2,"stats":{"get_bytes":3851,"get_count":4,"put_bytes":1887,"put_count":3}} +{"i":24,"series":2,"stats":{"get_bytes":3851,"get_count":4,"put_bytes":1886,"put_count":3}} +{"i":25,"series":2,"stats":{"get_bytes":3811,"get_count":4,"put_bytes":1846,"put_count":3}} +{"i":26,"series":2,"stats":{"get_bytes":3893,"get_count":4,"put_bytes":1928,"put_count":3}} +{"i":27,"series":2,"stats":{"get_bytes":3810,"get_count":4,"put_bytes":1899,"put_count":4}} +{"i":28,"series":2,"stats":{"get_bytes":3934,"get_count":4,"put_bytes":1969,"put_count":3}} +{"i":29,"series":2,"stats":{"get_bytes":3681,"get_count":3,"put_bytes":1772,"put_count":3}} +{"i":30,"series":2,"stats":{"get_bytes":3859,"get_count":4,"put_bytes":1894,"put_count":3}} +{"i":31,"series":2,"stats":{"get_bytes":3901,"get_count":4,"put_bytes":1936,"put_count":3}} +{"i":32,"series":2,"stats":{"get_bytes":3900,"get_count":4,"put_bytes":1936,"put_count":3}} +{"i":33,"series":2,"stats":{"get_bytes":3900,"get_count":4,"put_bytes":1935,"put_count":3}} +{"i":34,"series":2,"stats":{"get_bytes":3778,"get_count":4,"put_bytes":1813,"put_count":3}} +{"i":35,"series":2,"stats":{"get_bytes":3860,"get_count":4,"put_bytes":1895,"put_count":3}} +{"i":36,"series":2,"stats":{"get_bytes":3778,"get_count":4,"put_bytes":1813,"put_count":3}} +{"i":37,"series":2,"stats":{"get_bytes":3860,"get_count":4,"put_bytes":1951,"put_count":4}} +{"i":38,"series":2,"stats":{"get_bytes":3901,"get_count":4,"put_bytes":1936,"put_count":3}} +{"i":39,"series":2,"stats":{"get_bytes":3859,"get_count":4,"put_bytes":1894,"put_count":3}} +{"i":40,"series":2,"stats":{"get_bytes":3868,"get_count":4,"put_bytes":1903,"put_count":3}} +{"i":41,"series":2,"stats":{"get_bytes":3826,"get_count":4,"put_bytes":1861,"put_count":3}} +{"i":42,"series":2,"stats":{"get_bytes":3860,"get_count":4,"put_bytes":1949,"put_count":4}} +{"i":43,"series":2,"stats":{"get_bytes":3942,"get_count":4,"put_bytes":1977,"put_count":3}} +{"i":44,"series":2,"stats":{"get_bytes":3776,"get_count":4,"put_bytes":1813,"put_count":3}} +{"i":45,"series":2,"stats":{"get_bytes":3909,"get_count":4,"put_bytes":1944,"put_count":3}} +{"i":46,"series":2,"stats":{"get_bytes":3901,"get_count":4,"put_bytes":1936,"put_count":3}} +{"i":47,"series":2,"stats":{"get_bytes":3942,"get_count":4,"put_bytes":1977,"put_count":3}} +{"i":48,"series":2,"stats":{"get_bytes":3825,"get_count":4,"put_bytes":1862,"put_count":3}} +{"i":49,"series":2,"stats":{"get_bytes":3868,"get_count":4,"put_bytes":1903,"put_count":3}} +{"i":50,"series":2,"stats":{"get_bytes":3860,"get_count":4,"put_bytes":1895,"put_count":3}} +{"i":51,"series":2,"stats":{"get_bytes":3860,"get_count":4,"put_bytes":1949,"put_count":4}} +{"i":52,"series":2,"stats":{"get_bytes":3777,"get_count":4,"put_bytes":1813,"put_count":3}} +{"i":53,"series":2,"stats":{"get_bytes":3778,"get_count":4,"put_bytes":1813,"put_count":3}} +{"i":54,"series":2,"stats":{"get_bytes":3819,"get_count":4,"put_bytes":1909,"put_count":4}} +{"i":55,"series":2,"stats":{"get_bytes":3900,"get_count":4,"put_bytes":1991,"put_count":4}} +{"i":56,"series":2,"stats":{"get_bytes":3942,"get_count":4,"put_bytes":1977,"put_count":3}} +{"i":57,"series":2,"stats":{"get_bytes":3778,"get_count":4,"put_bytes":1866,"put_count":4}} +{"i":58,"series":2,"stats":{"get_bytes":3958,"get_count":4,"put_bytes":1993,"put_count":3}} +{"i":59,"series":2,"stats":{"get_bytes":3983,"get_count":4,"put_bytes":2073,"put_count":4}} +{"i":60,"series":2,"stats":{"get_bytes":3909,"get_count":4,"put_bytes":1944,"put_count":3}} +{"i":61,"series":2,"stats":{"get_bytes":3942,"get_count":4,"put_bytes":1977,"put_count":3}} +{"i":62,"series":2,"stats":{"get_bytes":3983,"get_count":4,"put_bytes":2073,"put_count":4}} +{"i":63,"series":2,"stats":{"get_bytes":3983,"get_count":4,"put_bytes":2073,"put_count":4}} +{"i":64,"series":2,"stats":{"get_bytes":3901,"get_count":4,"put_bytes":1992,"put_count":4}} +{"i":65,"series":2,"stats":{"get_bytes":3868,"get_count":4,"put_bytes":1903,"put_count":3}} +{"i":66,"series":2,"stats":{"get_bytes":3867,"get_count":4,"put_bytes":1957,"put_count":4}} +{"i":67,"series":2,"stats":{"get_bytes":3909,"get_count":4,"put_bytes":1944,"put_count":3}} +{"i":68,"series":2,"stats":{"get_bytes":3827,"get_count":4,"put_bytes":1862,"put_count":3}} +{"i":69,"series":2,"stats":{"get_bytes":3868,"get_count":4,"put_bytes":1903,"put_count":3}} +{"i":70,"series":2,"stats":{"get_bytes":3991,"get_count":4,"put_bytes":2026,"put_count":3}} +{"i":71,"series":2,"stats":{"get_bytes":3942,"get_count":4,"put_bytes":1977,"put_count":3}} +{"i":72,"series":2,"stats":{"get_bytes":3860,"get_count":4,"put_bytes":1895,"put_count":3}} +{"i":73,"series":2,"stats":{"get_bytes":3901,"get_count":4,"put_bytes":1936,"put_count":3}} +{"i":74,"series":2,"stats":{"get_bytes":3983,"get_count":4,"put_bytes":2018,"put_count":3}} +{"i":75,"series":2,"stats":{"get_bytes":3819,"get_count":4,"put_bytes":1854,"put_count":3}} +{"i":76,"series":2,"stats":{"get_bytes":3689,"get_count":3,"put_bytes":1780,"put_count":3}} +{"i":77,"series":2,"stats":{"get_bytes":3991,"get_count":4,"put_bytes":2026,"put_count":3}} +{"i":78,"series":2,"stats":{"get_bytes":3917,"get_count":4,"put_bytes":1952,"put_count":3}} +{"i":79,"series":2,"stats":{"get_bytes":3916,"get_count":4,"put_bytes":1951,"put_count":3}} +{"i":80,"series":2,"stats":{"get_bytes":3999,"get_count":4,"put_bytes":2034,"put_count":3}} +{"i":81,"series":2,"stats":{"get_bytes":3827,"get_count":4,"put_bytes":1862,"put_count":3}} +{"i":82,"series":2,"stats":{"get_bytes":3827,"get_count":4,"put_bytes":1862,"put_count":3}} +{"i":83,"series":2,"stats":{"get_bytes":3991,"get_count":4,"put_bytes":2081,"put_count":4}} +{"i":84,"series":2,"stats":{"get_bytes":3958,"get_count":4,"put_bytes":1993,"put_count":3}} +{"i":85,"series":2,"stats":{"get_bytes":3827,"get_count":4,"put_bytes":1862,"put_count":3}} +{"i":86,"series":2,"stats":{"get_bytes":3917,"get_count":4,"put_bytes":1952,"put_count":3}} +{"i":87,"series":2,"stats":{"get_bytes":3958,"get_count":4,"put_bytes":1993,"put_count":3}} +{"i":88,"series":2,"stats":{"get_bytes":3958,"get_count":4,"put_bytes":1993,"put_count":3}} +{"i":89,"series":2,"stats":{"get_bytes":3786,"get_count":4,"put_bytes":1821,"put_count":3}} +{"i":90,"series":2,"stats":{"get_bytes":3883,"get_count":4,"put_bytes":1919,"put_count":3}} +{"i":91,"series":2,"stats":{"get_bytes":3868,"get_count":4,"put_bytes":1903,"put_count":3}} +{"i":92,"series":2,"stats":{"get_bytes":3958,"get_count":4,"put_bytes":1993,"put_count":3}} +{"i":93,"series":2,"stats":{"get_bytes":3827,"get_count":4,"put_bytes":1862,"put_count":3}} +{"i":94,"series":2,"stats":{"get_bytes":3950,"get_count":4,"put_bytes":1985,"put_count":3}} +{"i":95,"series":2,"stats":{"get_bytes":3868,"get_count":4,"put_bytes":1903,"put_count":3}} +{"i":96,"series":2,"stats":{"get_bytes":3697,"get_count":3,"put_bytes":1786,"put_count":3}} +{"i":97,"series":2,"stats":{"get_bytes":3917,"get_count":4,"put_bytes":1952,"put_count":3}} +{"i":98,"series":2,"stats":{"get_bytes":3966,"get_count":4,"put_bytes":2001,"put_count":3}} +{"i":99,"series":2,"stats":{"get_bytes":3835,"get_count":4,"put_bytes":1870,"put_count":3}} diff --git a/actors/evm/tests/measurements/mapping_add_n1.png b/actors/evm/tests/measurements/mapping_add_n1.png new file mode 100644 index 000000000..c2dd47452 Binary files /dev/null and b/actors/evm/tests/measurements/mapping_add_n1.png differ diff --git a/actors/evm/tests/measurements/mapping_add_n100.jsonline b/actors/evm/tests/measurements/mapping_add_n100.jsonline new file mode 100644 index 000000000..82ead2085 --- /dev/null +++ b/actors/evm/tests/measurements/mapping_add_n100.jsonline @@ -0,0 +1,200 @@ +{"i":0,"series":1,"stats":{"get_bytes":2132,"get_count":3,"put_bytes":2132,"put_count":3}} +{"i":1,"series":1,"stats":{"get_bytes":2132,"get_count":3,"put_bytes":6059,"put_count":35}} +{"i":2,"series":1,"stats":{"get_bytes":7280,"get_count":28,"put_bytes":10271,"put_count":43}} +{"i":3,"series":1,"stats":{"get_bytes":10590,"get_count":32,"put_bytes":13517,"put_count":46}} +{"i":4,"series":1,"stats":{"get_bytes":14963,"get_count":37,"put_bytes":18822,"put_count":68}} +{"i":5,"series":1,"stats":{"get_bytes":17975,"get_count":43,"put_bytes":21287,"put_count":64}} +{"i":6,"series":1,"stats":{"get_bytes":19374,"get_count":38,"put_bytes":23461,"put_count":73}} +{"i":7,"series":1,"stats":{"get_bytes":23547,"get_count":47,"put_bytes":27579,"put_count":81}} +{"i":8,"series":1,"stats":{"get_bytes":27383,"get_count":48,"put_bytes":31406,"put_count":82}} +{"i":9,"series":1,"stats":{"get_bytes":29028,"get_count":55,"put_bytes":33209,"put_count":93}} +{"i":10,"series":1,"stats":{"get_bytes":31807,"get_count":49,"put_bytes":35728,"put_count":81}} +{"i":11,"series":1,"stats":{"get_bytes":34522,"get_count":54,"put_bytes":38862,"put_count":94}} +{"i":12,"series":1,"stats":{"get_bytes":37022,"get_count":70,"put_bytes":41207,"put_count":108}} +{"i":13,"series":1,"stats":{"get_bytes":36347,"get_count":68,"put_bytes":40355,"put_count":102}} +{"i":14,"series":1,"stats":{"get_bytes":39932,"get_count":67,"put_bytes":44368,"put_count":109}} +{"i":15,"series":1,"stats":{"get_bytes":40957,"get_count":71,"put_bytes":45103,"put_count":107}} +{"i":16,"series":1,"stats":{"get_bytes":42957,"get_count":72,"put_bytes":47345,"put_count":113}} +{"i":17,"series":1,"stats":{"get_bytes":43705,"get_count":74,"put_bytes":48073,"put_count":114}} +{"i":18,"series":1,"stats":{"get_bytes":42203,"get_count":75,"put_bytes":46485,"put_count":114}} +{"i":19,"series":1,"stats":{"get_bytes":50338,"get_count":92,"put_bytes":54039,"put_count":120}} +{"i":20,"series":1,"stats":{"get_bytes":45835,"get_count":81,"put_bytes":49764,"put_count":113}} +{"i":21,"series":1,"stats":{"get_bytes":47963,"get_count":80,"put_bytes":52393,"put_count":121}} +{"i":22,"series":1,"stats":{"get_bytes":48087,"get_count":83,"put_bytes":52126,"put_count":117}} +{"i":23,"series":1,"stats":{"get_bytes":55275,"get_count":108,"put_bytes":58095,"put_count":121}} +{"i":24,"series":1,"stats":{"get_bytes":54443,"get_count":104,"put_bytes":57787,"put_count":126}} +{"i":25,"series":1,"stats":{"get_bytes":54685,"get_count":102,"put_bytes":58422,"put_count":131}} +{"i":26,"series":1,"stats":{"get_bytes":53701,"get_count":94,"put_bytes":57798,"put_count":129}} +{"i":27,"series":1,"stats":{"get_bytes":56311,"get_count":107,"put_bytes":59471,"put_count":125}} +{"i":28,"series":1,"stats":{"get_bytes":56491,"get_count":106,"put_bytes":60200,"put_count":134}} +{"i":29,"series":1,"stats":{"get_bytes":56896,"get_count":106,"put_bytes":60003,"put_count":123}} +{"i":30,"series":1,"stats":{"get_bytes":61062,"get_count":113,"put_bytes":64690,"put_count":140}} +{"i":31,"series":1,"stats":{"get_bytes":56139,"get_count":107,"put_bytes":59588,"put_count":131}} +{"i":32,"series":1,"stats":{"get_bytes":61818,"get_count":111,"put_bytes":65267,"put_count":135}} +{"i":33,"series":1,"stats":{"get_bytes":59941,"get_count":114,"put_bytes":63552,"put_count":141}} +{"i":34,"series":1,"stats":{"get_bytes":61477,"get_count":113,"put_bytes":64497,"put_count":130}} +{"i":35,"series":1,"stats":{"get_bytes":61302,"get_count":116,"put_bytes":64224,"put_count":131}} +{"i":36,"series":1,"stats":{"get_bytes":62203,"get_count":113,"put_bytes":65210,"put_count":129}} +{"i":37,"series":1,"stats":{"get_bytes":63057,"get_count":117,"put_bytes":66386,"put_count":138}} +{"i":38,"series":1,"stats":{"get_bytes":64626,"get_count":120,"put_bytes":68083,"put_count":144}} +{"i":39,"series":1,"stats":{"get_bytes":60346,"get_count":115,"put_bytes":63342,"put_count":130}} +{"i":40,"series":1,"stats":{"get_bytes":64879,"get_count":121,"put_bytes":67750,"put_count":134}} +{"i":41,"series":1,"stats":{"get_bytes":64288,"get_count":118,"put_bytes":67341,"put_count":134}} +{"i":42,"series":1,"stats":{"get_bytes":66192,"get_count":118,"put_bytes":69621,"put_count":141}} +{"i":43,"series":1,"stats":{"get_bytes":64765,"get_count":121,"put_bytes":67779,"put_count":137}} +{"i":44,"series":1,"stats":{"get_bytes":69586,"get_count":127,"put_bytes":72362,"put_count":138}} +{"i":45,"series":1,"stats":{"get_bytes":67388,"get_count":123,"put_bytes":70665,"put_count":144}} +{"i":46,"series":1,"stats":{"get_bytes":69073,"get_count":128,"put_bytes":72275,"put_count":147}} +{"i":47,"series":1,"stats":{"get_bytes":69167,"get_count":128,"put_bytes":72266,"put_count":145}} +{"i":48,"series":1,"stats":{"get_bytes":71820,"get_count":125,"put_bytes":74761,"put_count":139}} +{"i":49,"series":1,"stats":{"get_bytes":70011,"get_count":127,"put_bytes":73164,"put_count":145}} +{"i":50,"series":1,"stats":{"get_bytes":67054,"get_count":117,"put_bytes":69983,"put_count":131}} +{"i":51,"series":1,"stats":{"get_bytes":69733,"get_count":119,"put_bytes":72610,"put_count":132}} +{"i":52,"series":1,"stats":{"get_bytes":70904,"get_count":128,"put_bytes":74050,"put_count":146}} +{"i":53,"series":1,"stats":{"get_bytes":67921,"get_count":122,"put_bytes":70628,"put_count":132}} +{"i":54,"series":1,"stats":{"get_bytes":72420,"get_count":130,"put_bytes":75467,"put_count":146}} +{"i":55,"series":1,"stats":{"get_bytes":70211,"get_count":120,"put_bytes":73048,"put_count":133}} +{"i":56,"series":1,"stats":{"get_bytes":70598,"get_count":127,"put_bytes":73586,"put_count":142}} +{"i":57,"series":1,"stats":{"get_bytes":74390,"get_count":132,"put_bytes":77130,"put_count":143}} +{"i":58,"series":1,"stats":{"get_bytes":71982,"get_count":129,"put_bytes":74939,"put_count":144}} +{"i":59,"series":1,"stats":{"get_bytes":73939,"get_count":135,"put_bytes":76533,"put_count":143}} +{"i":60,"series":1,"stats":{"get_bytes":72539,"get_count":129,"put_bytes":75580,"put_count":145}} +{"i":61,"series":1,"stats":{"get_bytes":76570,"get_count":131,"put_bytes":79500,"put_count":145}} +{"i":62,"series":1,"stats":{"get_bytes":70673,"get_count":127,"put_bytes":73448,"put_count":138}} +{"i":63,"series":1,"stats":{"get_bytes":77304,"get_count":134,"put_bytes":80243,"put_count":150}} +{"i":64,"series":1,"stats":{"get_bytes":77140,"get_count":131,"put_bytes":80132,"put_count":146}} +{"i":65,"series":1,"stats":{"get_bytes":71317,"get_count":126,"put_bytes":74467,"put_count":144}} +{"i":66,"series":1,"stats":{"get_bytes":76160,"get_count":128,"put_bytes":79363,"put_count":147}} +{"i":67,"series":1,"stats":{"get_bytes":76990,"get_count":134,"put_bytes":79756,"put_count":145}} +{"i":68,"series":1,"stats":{"get_bytes":78034,"get_count":130,"put_bytes":80965,"put_count":144}} +{"i":69,"series":1,"stats":{"get_bytes":77907,"get_count":131,"put_bytes":80941,"put_count":147}} +{"i":70,"series":1,"stats":{"get_bytes":79202,"get_count":131,"put_bytes":82341,"put_count":149}} +{"i":71,"series":1,"stats":{"get_bytes":76225,"get_count":126,"put_bytes":79429,"put_count":145}} +{"i":72,"series":1,"stats":{"get_bytes":76112,"get_count":131,"put_bytes":79148,"put_count":147}} +{"i":73,"series":1,"stats":{"get_bytes":77665,"get_count":132,"put_bytes":80367,"put_count":142}} +{"i":74,"series":1,"stats":{"get_bytes":74916,"get_count":127,"put_bytes":78011,"put_count":144}} +{"i":75,"series":1,"stats":{"get_bytes":81084,"get_count":134,"put_bytes":84235,"put_count":152}} +{"i":76,"series":1,"stats":{"get_bytes":79890,"get_count":131,"put_bytes":82929,"put_count":147}} +{"i":77,"series":1,"stats":{"get_bytes":77936,"get_count":132,"put_bytes":80533,"put_count":140}} +{"i":78,"series":1,"stats":{"get_bytes":79479,"get_count":130,"put_bytes":82349,"put_count":143}} +{"i":79,"series":1,"stats":{"get_bytes":79362,"get_count":132,"put_bytes":82785,"put_count":155}} +{"i":80,"series":1,"stats":{"get_bytes":80125,"get_count":133,"put_bytes":83269,"put_count":151}} +{"i":81,"series":1,"stats":{"get_bytes":82894,"get_count":138,"put_bytes":86039,"put_count":156}} +{"i":82,"series":1,"stats":{"get_bytes":79514,"get_count":127,"put_bytes":82994,"put_count":151}} +{"i":83,"series":1,"stats":{"get_bytes":77206,"get_count":123,"put_bytes":80133,"put_count":137}} +{"i":84,"series":1,"stats":{"get_bytes":85056,"get_count":134,"put_bytes":88247,"put_count":153}} +{"i":85,"series":1,"stats":{"get_bytes":81269,"get_count":129,"put_bytes":84138,"put_count":142}} +{"i":86,"series":1,"stats":{"get_bytes":83143,"get_count":131,"put_bytes":86073,"put_count":145}} +{"i":87,"series":1,"stats":{"get_bytes":81447,"get_count":129,"put_bytes":84594,"put_count":147}} +{"i":88,"series":1,"stats":{"get_bytes":82194,"get_count":131,"put_bytes":85665,"put_count":155}} +{"i":89,"series":1,"stats":{"get_bytes":85096,"get_count":133,"put_bytes":88621,"put_count":158}} +{"i":90,"series":1,"stats":{"get_bytes":87903,"get_count":135,"put_bytes":91270,"put_count":157}} +{"i":91,"series":1,"stats":{"get_bytes":82795,"get_count":135,"put_bytes":85996,"put_count":154}} +{"i":92,"series":1,"stats":{"get_bytes":87777,"get_count":132,"put_bytes":91254,"put_count":156}} +{"i":93,"series":1,"stats":{"get_bytes":83262,"get_count":130,"put_bytes":87177,"put_count":162}} +{"i":94,"series":1,"stats":{"get_bytes":85488,"get_count":130,"put_bytes":88736,"put_count":150}} +{"i":95,"series":1,"stats":{"get_bytes":85698,"get_count":132,"put_bytes":89180,"put_count":156}} +{"i":96,"series":1,"stats":{"get_bytes":83628,"get_count":134,"put_bytes":86498,"put_count":147}} +{"i":97,"series":1,"stats":{"get_bytes":84323,"get_count":135,"put_bytes":87697,"put_count":157}} +{"i":98,"series":1,"stats":{"get_bytes":83688,"get_count":133,"put_bytes":86883,"put_count":152}} +{"i":99,"series":1,"stats":{"get_bytes":86496,"get_count":135,"put_bytes":89589,"put_count":152}} +{"i":0,"series":2,"stats":{"get_bytes":88971,"get_count":140,"put_bytes":0,"put_count":0}} +{"i":1,"series":2,"stats":{"get_bytes":86989,"get_count":131,"put_bytes":90297,"put_count":152}} +{"i":2,"series":2,"stats":{"get_bytes":86665,"get_count":132,"put_bytes":90249,"put_count":158}} +{"i":3,"series":2,"stats":{"get_bytes":88117,"get_count":133,"put_bytes":91763,"put_count":160}} +{"i":4,"series":2,"stats":{"get_bytes":83222,"get_count":130,"put_bytes":86748,"put_count":155}} +{"i":5,"series":2,"stats":{"get_bytes":88111,"get_count":131,"put_bytes":91754,"put_count":158}} +{"i":6,"series":2,"stats":{"get_bytes":90630,"get_count":133,"put_bytes":94167,"put_count":158}} +{"i":7,"series":2,"stats":{"get_bytes":90520,"get_count":134,"put_bytes":94169,"put_count":161}} +{"i":8,"series":2,"stats":{"get_bytes":92695,"get_count":138,"put_bytes":96226,"put_count":163}} +{"i":9,"series":2,"stats":{"get_bytes":91635,"get_count":134,"put_bytes":95058,"put_count":157}} +{"i":10,"series":2,"stats":{"get_bytes":88286,"get_count":131,"put_bytes":91708,"put_count":154}} +{"i":11,"series":2,"stats":{"get_bytes":91069,"get_count":137,"put_bytes":94324,"put_count":157}} +{"i":12,"series":2,"stats":{"get_bytes":88845,"get_count":130,"put_bytes":92307,"put_count":154}} +{"i":13,"series":2,"stats":{"get_bytes":88800,"get_count":132,"put_bytes":92279,"put_count":156}} +{"i":14,"series":2,"stats":{"get_bytes":92331,"get_count":135,"put_bytes":95957,"put_count":162}} +{"i":15,"series":2,"stats":{"get_bytes":82486,"get_count":127,"put_bytes":85574,"put_count":144}} +{"i":16,"series":2,"stats":{"get_bytes":92258,"get_count":134,"put_bytes":95630,"put_count":156}} +{"i":17,"series":2,"stats":{"get_bytes":93130,"get_count":136,"put_bytes":96383,"put_count":156}} +{"i":18,"series":2,"stats":{"get_bytes":93812,"get_count":135,"put_bytes":97513,"put_count":163}} +{"i":19,"series":2,"stats":{"get_bytes":90550,"get_count":129,"put_bytes":93590,"put_count":145}} +{"i":20,"series":2,"stats":{"get_bytes":91605,"get_count":132,"put_bytes":95079,"put_count":156}} +{"i":21,"series":2,"stats":{"get_bytes":97155,"get_count":139,"put_bytes":100745,"put_count":165}} +{"i":22,"series":2,"stats":{"get_bytes":93935,"get_count":135,"put_bytes":97466,"put_count":160}} +{"i":23,"series":2,"stats":{"get_bytes":92033,"get_count":131,"put_bytes":95401,"put_count":153}} +{"i":24,"series":2,"stats":{"get_bytes":93461,"get_count":131,"put_bytes":97039,"put_count":157}} +{"i":25,"series":2,"stats":{"get_bytes":94598,"get_count":129,"put_bytes":98040,"put_count":153}} +{"i":26,"series":2,"stats":{"get_bytes":96792,"get_count":139,"put_bytes":99999,"put_count":158}} +{"i":27,"series":2,"stats":{"get_bytes":97665,"get_count":138,"put_bytes":101048,"put_count":161}} +{"i":28,"series":2,"stats":{"get_bytes":96494,"get_count":135,"put_bytes":100291,"put_count":165}} +{"i":29,"series":2,"stats":{"get_bytes":90705,"get_count":133,"put_bytes":93913,"put_count":152}} +{"i":30,"series":2,"stats":{"get_bytes":94476,"get_count":136,"put_bytes":98173,"put_count":164}} +{"i":31,"series":2,"stats":{"get_bytes":97254,"get_count":137,"put_bytes":101003,"put_count":166}} +{"i":32,"series":2,"stats":{"get_bytes":97928,"get_count":139,"put_bytes":101899,"put_count":172}} +{"i":33,"series":2,"stats":{"get_bytes":92898,"get_count":131,"put_bytes":96677,"put_count":161}} +{"i":34,"series":2,"stats":{"get_bytes":97463,"get_count":136,"put_bytes":101043,"put_count":162}} +{"i":35,"series":2,"stats":{"get_bytes":98816,"get_count":133,"put_bytes":102423,"put_count":160}} +{"i":36,"series":2,"stats":{"get_bytes":98276,"get_count":134,"put_bytes":102415,"put_count":170}} +{"i":37,"series":2,"stats":{"get_bytes":97042,"get_count":132,"put_bytes":100576,"put_count":157}} +{"i":38,"series":2,"stats":{"get_bytes":94967,"get_count":131,"put_bytes":99060,"put_count":166}} +{"i":39,"series":2,"stats":{"get_bytes":93264,"get_count":131,"put_bytes":96512,"put_count":151}} +{"i":40,"series":2,"stats":{"get_bytes":93835,"get_count":131,"put_bytes":97471,"put_count":158}} +{"i":41,"series":2,"stats":{"get_bytes":98110,"get_count":143,"put_bytes":101585,"put_count":167}} +{"i":42,"series":2,"stats":{"get_bytes":93676,"get_count":127,"put_bytes":97484,"put_count":157}} +{"i":43,"series":2,"stats":{"get_bytes":98978,"get_count":131,"put_bytes":102225,"put_count":151}} +{"i":44,"series":2,"stats":{"get_bytes":99367,"get_count":137,"put_bytes":102791,"put_count":160}} +{"i":45,"series":2,"stats":{"get_bytes":102314,"get_count":142,"put_bytes":106022,"put_count":170}} +{"i":46,"series":2,"stats":{"get_bytes":101496,"get_count":140,"put_bytes":104803,"put_count":161}} +{"i":47,"series":2,"stats":{"get_bytes":99665,"get_count":135,"put_bytes":103634,"put_count":168}} +{"i":48,"series":2,"stats":{"get_bytes":100275,"get_count":137,"put_bytes":104247,"put_count":170}} +{"i":49,"series":2,"stats":{"get_bytes":101444,"get_count":136,"put_bytes":105081,"put_count":163}} +{"i":50,"series":2,"stats":{"get_bytes":99249,"get_count":136,"put_bytes":102673,"put_count":159}} +{"i":51,"series":2,"stats":{"get_bytes":101479,"get_count":139,"put_bytes":104565,"put_count":156}} +{"i":52,"series":2,"stats":{"get_bytes":103596,"get_count":144,"put_bytes":107563,"put_count":177}} +{"i":53,"series":2,"stats":{"get_bytes":97678,"get_count":129,"put_bytes":101260,"put_count":155}} +{"i":54,"series":2,"stats":{"get_bytes":100732,"get_count":142,"put_bytes":104434,"put_count":170}} +{"i":55,"series":2,"stats":{"get_bytes":99906,"get_count":130,"put_bytes":103490,"put_count":156}} +{"i":56,"series":2,"stats":{"get_bytes":101667,"get_count":140,"put_bytes":105581,"put_count":172}} +{"i":57,"series":2,"stats":{"get_bytes":101450,"get_count":139,"put_bytes":105222,"put_count":169}} +{"i":58,"series":2,"stats":{"get_bytes":107312,"get_count":139,"put_bytes":111346,"put_count":173}} +{"i":59,"series":2,"stats":{"get_bytes":106206,"get_count":133,"put_bytes":110666,"put_count":175}} +{"i":60,"series":2,"stats":{"get_bytes":107510,"get_count":136,"put_bytes":111587,"put_count":171}} +{"i":61,"series":2,"stats":{"get_bytes":106968,"get_count":134,"put_bytes":111225,"put_count":172}} +{"i":62,"series":2,"stats":{"get_bytes":101581,"get_count":131,"put_bytes":105447,"put_count":162}} +{"i":63,"series":2,"stats":{"get_bytes":99591,"get_count":138,"put_bytes":103591,"put_count":172}} +{"i":64,"series":2,"stats":{"get_bytes":105297,"get_count":139,"put_bytes":109214,"put_count":171}} +{"i":65,"series":2,"stats":{"get_bytes":106399,"get_count":137,"put_bytes":110370,"put_count":170}} +{"i":66,"series":2,"stats":{"get_bytes":105790,"get_count":138,"put_bytes":109548,"put_count":167}} +{"i":67,"series":2,"stats":{"get_bytes":107863,"get_count":144,"put_bytes":111557,"put_count":172}} +{"i":68,"series":2,"stats":{"get_bytes":108226,"get_count":140,"put_bytes":112023,"put_count":170}} +{"i":69,"series":2,"stats":{"get_bytes":105410,"get_count":140,"put_bytes":109412,"put_count":174}} +{"i":70,"series":2,"stats":{"get_bytes":108240,"get_count":142,"put_bytes":111660,"put_count":165}} +{"i":71,"series":2,"stats":{"get_bytes":105938,"get_count":134,"put_bytes":109740,"put_count":164}} +{"i":72,"series":2,"stats":{"get_bytes":104964,"get_count":135,"put_bytes":108772,"put_count":165}} +{"i":73,"series":2,"stats":{"get_bytes":105965,"get_count":140,"put_bytes":110270,"put_count":179}} +{"i":74,"series":2,"stats":{"get_bytes":103447,"get_count":134,"put_bytes":107424,"put_count":167}} +{"i":75,"series":2,"stats":{"get_bytes":105510,"get_count":141,"put_bytes":109151,"put_count":168}} +{"i":76,"series":2,"stats":{"get_bytes":107462,"get_count":142,"put_bytes":111549,"put_count":177}} +{"i":77,"series":2,"stats":{"get_bytes":110882,"get_count":135,"put_bytes":114843,"put_count":168}} +{"i":78,"series":2,"stats":{"get_bytes":108557,"get_count":140,"put_bytes":112429,"put_count":171}} +{"i":79,"series":2,"stats":{"get_bytes":108809,"get_count":134,"put_bytes":112903,"put_count":169}} +{"i":80,"series":2,"stats":{"get_bytes":110952,"get_count":143,"put_bytes":115082,"put_count":179}} +{"i":81,"series":2,"stats":{"get_bytes":106131,"get_count":137,"put_bytes":109889,"put_count":166}} +{"i":82,"series":2,"stats":{"get_bytes":112042,"get_count":145,"put_bytes":115906,"put_count":176}} +{"i":83,"series":2,"stats":{"get_bytes":109100,"get_count":136,"put_bytes":113411,"put_count":175}} +{"i":84,"series":2,"stats":{"get_bytes":107994,"get_count":136,"put_bytes":111714,"put_count":165}} +{"i":85,"series":2,"stats":{"get_bytes":108643,"get_count":137,"put_bytes":112617,"put_count":170}} +{"i":86,"series":2,"stats":{"get_bytes":106873,"get_count":142,"put_bytes":110964,"put_count":177}} +{"i":87,"series":2,"stats":{"get_bytes":113453,"get_count":140,"put_bytes":117489,"put_count":174}} +{"i":88,"series":2,"stats":{"get_bytes":109120,"get_count":138,"put_bytes":113031,"put_count":170}} +{"i":89,"series":2,"stats":{"get_bytes":106677,"get_count":140,"put_bytes":110326,"put_count":167}} +{"i":90,"series":2,"stats":{"get_bytes":108676,"get_count":141,"put_bytes":112317,"put_count":168}} +{"i":91,"series":2,"stats":{"get_bytes":113648,"get_count":143,"put_bytes":117213,"put_count":170}} +{"i":92,"series":2,"stats":{"get_bytes":109181,"get_count":140,"put_bytes":113010,"put_count":171}} +{"i":93,"series":2,"stats":{"get_bytes":109738,"get_count":140,"put_bytes":113978,"put_count":179}} +{"i":94,"series":2,"stats":{"get_bytes":111825,"get_count":143,"put_bytes":115576,"put_count":172}} +{"i":95,"series":2,"stats":{"get_bytes":109812,"get_count":142,"put_bytes":113561,"put_count":171}} +{"i":96,"series":2,"stats":{"get_bytes":113340,"get_count":142,"put_bytes":117333,"put_count":176}} +{"i":97,"series":2,"stats":{"get_bytes":113104,"get_count":141,"put_bytes":117137,"put_count":175}} +{"i":98,"series":2,"stats":{"get_bytes":115773,"get_count":146,"put_bytes":119253,"put_count":170}} +{"i":99,"series":2,"stats":{"get_bytes":107139,"get_count":139,"put_bytes":111111,"put_count":172}} diff --git a/actors/evm/tests/measurements/mapping_add_n100.png b/actors/evm/tests/measurements/mapping_add_n100.png new file mode 100644 index 000000000..a1559a589 Binary files /dev/null and b/actors/evm/tests/measurements/mapping_add_n100.png differ diff --git a/actors/evm/tests/measurements/mapping_overwrite.jsonline b/actors/evm/tests/measurements/mapping_overwrite.jsonline new file mode 100644 index 000000000..87e726107 --- /dev/null +++ b/actors/evm/tests/measurements/mapping_overwrite.jsonline @@ -0,0 +1,200 @@ +{"i":0,"series":1,"stats":{"get_bytes":11809,"get_count":20,"put_bytes":9803,"put_count":19}} +{"i":1,"series":1,"stats":{"get_bytes":12428,"get_count":15,"put_bytes":10422,"put_count":14}} +{"i":2,"series":1,"stats":{"get_bytes":11034,"get_count":14,"put_bytes":9028,"put_count":13}} +{"i":3,"series":1,"stats":{"get_bytes":12467,"get_count":20,"put_bytes":10461,"put_count":19}} +{"i":4,"series":1,"stats":{"get_bytes":11878,"get_count":18,"put_bytes":9872,"put_count":17}} +{"i":5,"series":1,"stats":{"get_bytes":12692,"get_count":16,"put_bytes":10686,"put_count":15}} +{"i":6,"series":1,"stats":{"get_bytes":12808,"get_count":20,"put_bytes":10802,"put_count":19}} +{"i":7,"series":1,"stats":{"get_bytes":12542,"get_count":17,"put_bytes":10536,"put_count":16}} +{"i":8,"series":1,"stats":{"get_bytes":12711,"get_count":21,"put_bytes":10705,"put_count":20}} +{"i":9,"series":1,"stats":{"get_bytes":12241,"get_count":16,"put_bytes":10235,"put_count":15}} +{"i":10,"series":1,"stats":{"get_bytes":13554,"get_count":20,"put_bytes":11548,"put_count":19}} +{"i":11,"series":1,"stats":{"get_bytes":11306,"get_count":14,"put_bytes":9300,"put_count":13}} +{"i":12,"series":1,"stats":{"get_bytes":11708,"get_count":18,"put_bytes":9702,"put_count":17}} +{"i":13,"series":1,"stats":{"get_bytes":13212,"get_count":18,"put_bytes":11206,"put_count":17}} +{"i":14,"series":1,"stats":{"get_bytes":12531,"get_count":18,"put_bytes":10525,"put_count":17}} +{"i":15,"series":1,"stats":{"get_bytes":11846,"get_count":17,"put_bytes":9840,"put_count":16}} +{"i":16,"series":1,"stats":{"get_bytes":10175,"get_count":13,"put_bytes":8169,"put_count":12}} +{"i":17,"series":1,"stats":{"get_bytes":10920,"get_count":17,"put_bytes":8914,"put_count":16}} +{"i":18,"series":1,"stats":{"get_bytes":13251,"get_count":18,"put_bytes":11245,"put_count":17}} +{"i":19,"series":1,"stats":{"get_bytes":12509,"get_count":19,"put_bytes":10503,"put_count":18}} +{"i":20,"series":1,"stats":{"get_bytes":12887,"get_count":20,"put_bytes":10881,"put_count":19}} +{"i":21,"series":1,"stats":{"get_bytes":11595,"get_count":17,"put_bytes":9589,"put_count":16}} +{"i":22,"series":1,"stats":{"get_bytes":13570,"get_count":19,"put_bytes":11564,"put_count":18}} +{"i":23,"series":1,"stats":{"get_bytes":11051,"get_count":19,"put_bytes":9045,"put_count":18}} +{"i":24,"series":1,"stats":{"get_bytes":12681,"get_count":18,"put_bytes":10675,"put_count":17}} +{"i":25,"series":1,"stats":{"get_bytes":12825,"get_count":18,"put_bytes":10819,"put_count":17}} +{"i":26,"series":1,"stats":{"get_bytes":11980,"get_count":18,"put_bytes":9974,"put_count":17}} +{"i":27,"series":1,"stats":{"get_bytes":11529,"get_count":14,"put_bytes":9523,"put_count":13}} +{"i":28,"series":1,"stats":{"get_bytes":13050,"get_count":19,"put_bytes":11044,"put_count":18}} +{"i":29,"series":1,"stats":{"get_bytes":12848,"get_count":18,"put_bytes":10842,"put_count":17}} +{"i":30,"series":1,"stats":{"get_bytes":13600,"get_count":21,"put_bytes":11594,"put_count":20}} +{"i":31,"series":1,"stats":{"get_bytes":11833,"get_count":16,"put_bytes":9827,"put_count":15}} +{"i":32,"series":1,"stats":{"get_bytes":12972,"get_count":19,"put_bytes":10966,"put_count":18}} +{"i":33,"series":1,"stats":{"get_bytes":12457,"get_count":19,"put_bytes":10451,"put_count":18}} +{"i":34,"series":1,"stats":{"get_bytes":11967,"get_count":15,"put_bytes":9961,"put_count":14}} +{"i":35,"series":1,"stats":{"get_bytes":12548,"get_count":19,"put_bytes":10542,"put_count":18}} +{"i":36,"series":1,"stats":{"get_bytes":13418,"get_count":19,"put_bytes":11412,"put_count":18}} +{"i":37,"series":1,"stats":{"get_bytes":11450,"get_count":16,"put_bytes":9444,"put_count":15}} +{"i":38,"series":1,"stats":{"get_bytes":11030,"get_count":16,"put_bytes":9024,"put_count":15}} +{"i":39,"series":1,"stats":{"get_bytes":12571,"get_count":18,"put_bytes":10565,"put_count":17}} +{"i":40,"series":1,"stats":{"get_bytes":12339,"get_count":22,"put_bytes":10333,"put_count":21}} +{"i":41,"series":1,"stats":{"get_bytes":13925,"get_count":20,"put_bytes":11919,"put_count":19}} +{"i":42,"series":1,"stats":{"get_bytes":12772,"get_count":20,"put_bytes":10766,"put_count":19}} +{"i":43,"series":1,"stats":{"get_bytes":12948,"get_count":17,"put_bytes":10942,"put_count":16}} +{"i":44,"series":1,"stats":{"get_bytes":13759,"get_count":21,"put_bytes":11753,"put_count":20}} +{"i":45,"series":1,"stats":{"get_bytes":11524,"get_count":17,"put_bytes":9518,"put_count":16}} +{"i":46,"series":1,"stats":{"get_bytes":12572,"get_count":18,"put_bytes":10566,"put_count":17}} +{"i":47,"series":1,"stats":{"get_bytes":13568,"get_count":19,"put_bytes":11562,"put_count":18}} +{"i":48,"series":1,"stats":{"get_bytes":12758,"get_count":20,"put_bytes":10752,"put_count":19}} +{"i":49,"series":1,"stats":{"get_bytes":12420,"get_count":20,"put_bytes":10414,"put_count":19}} +{"i":50,"series":1,"stats":{"get_bytes":13331,"get_count":19,"put_bytes":11325,"put_count":18}} +{"i":51,"series":1,"stats":{"get_bytes":11523,"get_count":15,"put_bytes":9517,"put_count":14}} +{"i":52,"series":1,"stats":{"get_bytes":11602,"get_count":17,"put_bytes":9596,"put_count":16}} +{"i":53,"series":1,"stats":{"get_bytes":12754,"get_count":18,"put_bytes":10748,"put_count":17}} +{"i":54,"series":1,"stats":{"get_bytes":13099,"get_count":22,"put_bytes":11093,"put_count":21}} +{"i":55,"series":1,"stats":{"get_bytes":10931,"get_count":17,"put_bytes":8925,"put_count":16}} +{"i":56,"series":1,"stats":{"get_bytes":11961,"get_count":17,"put_bytes":9955,"put_count":16}} +{"i":57,"series":1,"stats":{"get_bytes":13219,"get_count":17,"put_bytes":11213,"put_count":16}} +{"i":58,"series":1,"stats":{"get_bytes":13610,"get_count":21,"put_bytes":11604,"put_count":20}} +{"i":59,"series":1,"stats":{"get_bytes":12966,"get_count":16,"put_bytes":10960,"put_count":15}} +{"i":60,"series":1,"stats":{"get_bytes":12672,"get_count":18,"put_bytes":10666,"put_count":17}} +{"i":61,"series":1,"stats":{"get_bytes":14106,"get_count":23,"put_bytes":12100,"put_count":22}} +{"i":62,"series":1,"stats":{"get_bytes":12538,"get_count":18,"put_bytes":10532,"put_count":17}} +{"i":63,"series":1,"stats":{"get_bytes":13369,"get_count":19,"put_bytes":11363,"put_count":18}} +{"i":64,"series":1,"stats":{"get_bytes":11957,"get_count":19,"put_bytes":9951,"put_count":18}} +{"i":65,"series":1,"stats":{"get_bytes":12204,"get_count":19,"put_bytes":10198,"put_count":18}} +{"i":66,"series":1,"stats":{"get_bytes":11007,"get_count":16,"put_bytes":9001,"put_count":15}} +{"i":67,"series":1,"stats":{"get_bytes":12712,"get_count":20,"put_bytes":10706,"put_count":19}} +{"i":68,"series":1,"stats":{"get_bytes":12445,"get_count":17,"put_bytes":10439,"put_count":16}} +{"i":69,"series":1,"stats":{"get_bytes":11003,"get_count":17,"put_bytes":8997,"put_count":16}} +{"i":70,"series":1,"stats":{"get_bytes":13525,"get_count":18,"put_bytes":11519,"put_count":17}} +{"i":71,"series":1,"stats":{"get_bytes":10398,"get_count":16,"put_bytes":8392,"put_count":15}} +{"i":72,"series":1,"stats":{"get_bytes":12080,"get_count":15,"put_bytes":10074,"put_count":14}} +{"i":73,"series":1,"stats":{"get_bytes":12760,"get_count":19,"put_bytes":10754,"put_count":18}} +{"i":74,"series":1,"stats":{"get_bytes":12964,"get_count":20,"put_bytes":10958,"put_count":19}} +{"i":75,"series":1,"stats":{"get_bytes":11603,"get_count":18,"put_bytes":9597,"put_count":17}} +{"i":76,"series":1,"stats":{"get_bytes":12738,"get_count":19,"put_bytes":10732,"put_count":18}} +{"i":77,"series":1,"stats":{"get_bytes":13252,"get_count":18,"put_bytes":11246,"put_count":17}} +{"i":78,"series":1,"stats":{"get_bytes":12523,"get_count":18,"put_bytes":10517,"put_count":17}} +{"i":79,"series":1,"stats":{"get_bytes":12874,"get_count":21,"put_bytes":10868,"put_count":20}} +{"i":80,"series":1,"stats":{"get_bytes":12215,"get_count":18,"put_bytes":10209,"put_count":17}} +{"i":81,"series":1,"stats":{"get_bytes":11586,"get_count":18,"put_bytes":9580,"put_count":17}} +{"i":82,"series":1,"stats":{"get_bytes":11676,"get_count":19,"put_bytes":9670,"put_count":18}} +{"i":83,"series":1,"stats":{"get_bytes":13731,"get_count":17,"put_bytes":11725,"put_count":16}} +{"i":84,"series":1,"stats":{"get_bytes":12401,"get_count":18,"put_bytes":10395,"put_count":17}} +{"i":85,"series":1,"stats":{"get_bytes":12106,"get_count":17,"put_bytes":10100,"put_count":16}} +{"i":86,"series":1,"stats":{"get_bytes":12476,"get_count":18,"put_bytes":10470,"put_count":17}} +{"i":87,"series":1,"stats":{"get_bytes":12099,"get_count":16,"put_bytes":10093,"put_count":15}} +{"i":88,"series":1,"stats":{"get_bytes":13397,"get_count":20,"put_bytes":11391,"put_count":19}} +{"i":89,"series":1,"stats":{"get_bytes":13565,"get_count":18,"put_bytes":11559,"put_count":17}} +{"i":90,"series":1,"stats":{"get_bytes":13066,"get_count":19,"put_bytes":11060,"put_count":18}} +{"i":91,"series":1,"stats":{"get_bytes":12974,"get_count":18,"put_bytes":10968,"put_count":17}} +{"i":92,"series":1,"stats":{"get_bytes":11513,"get_count":16,"put_bytes":9507,"put_count":15}} +{"i":93,"series":1,"stats":{"get_bytes":12800,"get_count":18,"put_bytes":10794,"put_count":17}} +{"i":94,"series":1,"stats":{"get_bytes":11695,"get_count":18,"put_bytes":9689,"put_count":17}} +{"i":95,"series":1,"stats":{"get_bytes":11470,"get_count":17,"put_bytes":9464,"put_count":16}} +{"i":96,"series":1,"stats":{"get_bytes":11568,"get_count":19,"put_bytes":9562,"put_count":18}} +{"i":97,"series":1,"stats":{"get_bytes":11705,"get_count":16,"put_bytes":9699,"put_count":15}} +{"i":98,"series":1,"stats":{"get_bytes":12677,"get_count":20,"put_bytes":10671,"put_count":19}} +{"i":99,"series":1,"stats":{"get_bytes":12510,"get_count":21,"put_bytes":10504,"put_count":20}} +{"i":0,"series":2,"stats":{"get_bytes":11809,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":1,"series":2,"stats":{"get_bytes":12428,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":2,"series":2,"stats":{"get_bytes":11034,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":3,"series":2,"stats":{"get_bytes":12467,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":4,"series":2,"stats":{"get_bytes":11878,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":5,"series":2,"stats":{"get_bytes":12692,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":6,"series":2,"stats":{"get_bytes":12808,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":7,"series":2,"stats":{"get_bytes":12542,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":8,"series":2,"stats":{"get_bytes":12711,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":9,"series":2,"stats":{"get_bytes":12241,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":10,"series":2,"stats":{"get_bytes":13554,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":11,"series":2,"stats":{"get_bytes":11306,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":12,"series":2,"stats":{"get_bytes":11708,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":13,"series":2,"stats":{"get_bytes":13212,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":14,"series":2,"stats":{"get_bytes":12531,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":15,"series":2,"stats":{"get_bytes":11846,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":16,"series":2,"stats":{"get_bytes":10175,"get_count":13,"put_bytes":0,"put_count":0}} +{"i":17,"series":2,"stats":{"get_bytes":10920,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":18,"series":2,"stats":{"get_bytes":13251,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":19,"series":2,"stats":{"get_bytes":12509,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":20,"series":2,"stats":{"get_bytes":12887,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":21,"series":2,"stats":{"get_bytes":11595,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":22,"series":2,"stats":{"get_bytes":13570,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":23,"series":2,"stats":{"get_bytes":11051,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":24,"series":2,"stats":{"get_bytes":12681,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":25,"series":2,"stats":{"get_bytes":12825,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":26,"series":2,"stats":{"get_bytes":11980,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":27,"series":2,"stats":{"get_bytes":11529,"get_count":14,"put_bytes":0,"put_count":0}} +{"i":28,"series":2,"stats":{"get_bytes":13050,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":29,"series":2,"stats":{"get_bytes":12848,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":30,"series":2,"stats":{"get_bytes":13600,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":31,"series":2,"stats":{"get_bytes":11833,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":32,"series":2,"stats":{"get_bytes":12972,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":33,"series":2,"stats":{"get_bytes":12457,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":34,"series":2,"stats":{"get_bytes":11967,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":35,"series":2,"stats":{"get_bytes":12548,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":36,"series":2,"stats":{"get_bytes":13418,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":37,"series":2,"stats":{"get_bytes":11450,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":38,"series":2,"stats":{"get_bytes":11030,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":39,"series":2,"stats":{"get_bytes":12571,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":40,"series":2,"stats":{"get_bytes":12339,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":41,"series":2,"stats":{"get_bytes":13925,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":42,"series":2,"stats":{"get_bytes":12772,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":43,"series":2,"stats":{"get_bytes":12948,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":44,"series":2,"stats":{"get_bytes":13759,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":45,"series":2,"stats":{"get_bytes":11524,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":46,"series":2,"stats":{"get_bytes":12572,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":47,"series":2,"stats":{"get_bytes":13568,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":48,"series":2,"stats":{"get_bytes":12758,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":49,"series":2,"stats":{"get_bytes":12420,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":50,"series":2,"stats":{"get_bytes":13331,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":51,"series":2,"stats":{"get_bytes":11523,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":52,"series":2,"stats":{"get_bytes":11602,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":53,"series":2,"stats":{"get_bytes":12754,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":54,"series":2,"stats":{"get_bytes":13099,"get_count":22,"put_bytes":0,"put_count":0}} +{"i":55,"series":2,"stats":{"get_bytes":10931,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":56,"series":2,"stats":{"get_bytes":11961,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":57,"series":2,"stats":{"get_bytes":13219,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":58,"series":2,"stats":{"get_bytes":13610,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":59,"series":2,"stats":{"get_bytes":12966,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":60,"series":2,"stats":{"get_bytes":12672,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":61,"series":2,"stats":{"get_bytes":14106,"get_count":23,"put_bytes":0,"put_count":0}} +{"i":62,"series":2,"stats":{"get_bytes":12538,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":63,"series":2,"stats":{"get_bytes":13369,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":64,"series":2,"stats":{"get_bytes":11957,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":65,"series":2,"stats":{"get_bytes":12204,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":66,"series":2,"stats":{"get_bytes":11007,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":67,"series":2,"stats":{"get_bytes":12712,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":68,"series":2,"stats":{"get_bytes":12445,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":69,"series":2,"stats":{"get_bytes":11003,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":70,"series":2,"stats":{"get_bytes":13525,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":71,"series":2,"stats":{"get_bytes":10398,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":72,"series":2,"stats":{"get_bytes":12080,"get_count":15,"put_bytes":0,"put_count":0}} +{"i":73,"series":2,"stats":{"get_bytes":12760,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":74,"series":2,"stats":{"get_bytes":12964,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":75,"series":2,"stats":{"get_bytes":11603,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":76,"series":2,"stats":{"get_bytes":12738,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":77,"series":2,"stats":{"get_bytes":13252,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":78,"series":2,"stats":{"get_bytes":12523,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":79,"series":2,"stats":{"get_bytes":12874,"get_count":21,"put_bytes":0,"put_count":0}} +{"i":80,"series":2,"stats":{"get_bytes":12215,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":81,"series":2,"stats":{"get_bytes":11586,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":82,"series":2,"stats":{"get_bytes":11676,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":83,"series":2,"stats":{"get_bytes":13731,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":84,"series":2,"stats":{"get_bytes":12401,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":85,"series":2,"stats":{"get_bytes":12106,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":86,"series":2,"stats":{"get_bytes":12476,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":87,"series":2,"stats":{"get_bytes":12099,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":88,"series":2,"stats":{"get_bytes":13397,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":89,"series":2,"stats":{"get_bytes":13565,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":90,"series":2,"stats":{"get_bytes":13066,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":91,"series":2,"stats":{"get_bytes":12974,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":92,"series":2,"stats":{"get_bytes":11513,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":93,"series":2,"stats":{"get_bytes":12800,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":94,"series":2,"stats":{"get_bytes":11695,"get_count":18,"put_bytes":0,"put_count":0}} +{"i":95,"series":2,"stats":{"get_bytes":11470,"get_count":17,"put_bytes":0,"put_count":0}} +{"i":96,"series":2,"stats":{"get_bytes":11568,"get_count":19,"put_bytes":0,"put_count":0}} +{"i":97,"series":2,"stats":{"get_bytes":11705,"get_count":16,"put_bytes":0,"put_count":0}} +{"i":98,"series":2,"stats":{"get_bytes":12677,"get_count":20,"put_bytes":0,"put_count":0}} +{"i":99,"series":2,"stats":{"get_bytes":12510,"get_count":21,"put_bytes":0,"put_count":0}} diff --git a/actors/evm/tests/measurements/mapping_overwrite.png b/actors/evm/tests/measurements/mapping_overwrite.png new file mode 100644 index 000000000..1d9934527 Binary files /dev/null and b/actors/evm/tests/measurements/mapping_overwrite.png differ diff --git a/actors/evm/tests/measurements/mapping_read_n1.jsonline b/actors/evm/tests/measurements/mapping_read_n1.jsonline new file mode 100644 index 000000000..60f39f50c --- /dev/null +++ b/actors/evm/tests/measurements/mapping_read_n1.jsonline @@ -0,0 +1,100 @@ +{"i":0,"series":1,"stats":{"get_bytes":5583,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":1,"series":1,"stats":{"get_bytes":5739,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":2,"series":1,"stats":{"get_bytes":5706,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":3,"series":1,"stats":{"get_bytes":5836,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":4,"series":1,"stats":{"get_bytes":5706,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":5,"series":1,"stats":{"get_bytes":5616,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":6,"series":1,"stats":{"get_bytes":5812,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":7,"series":1,"stats":{"get_bytes":5859,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":8,"series":1,"stats":{"get_bytes":5795,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":9,"series":1,"stats":{"get_bytes":5853,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":10,"series":1,"stats":{"get_bytes":5616,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":11,"series":1,"stats":{"get_bytes":5723,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":12,"series":1,"stats":{"get_bytes":5841,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":13,"series":1,"stats":{"get_bytes":5686,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":14,"series":1,"stats":{"get_bytes":5548,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":15,"series":1,"stats":{"get_bytes":5534,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":16,"series":1,"stats":{"get_bytes":5754,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":17,"series":1,"stats":{"get_bytes":5960,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":18,"series":1,"stats":{"get_bytes":5591,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":19,"series":1,"stats":{"get_bytes":5941,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":20,"series":1,"stats":{"get_bytes":5583,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":21,"series":1,"stats":{"get_bytes":5657,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":22,"series":1,"stats":{"get_bytes":5542,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":23,"series":1,"stats":{"get_bytes":5616,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":24,"series":1,"stats":{"get_bytes":5754,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":25,"series":1,"stats":{"get_bytes":5722,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":26,"series":1,"stats":{"get_bytes":5761,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":27,"series":1,"stats":{"get_bytes":5729,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":28,"series":1,"stats":{"get_bytes":5542,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":29,"series":1,"stats":{"get_bytes":5739,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":30,"series":1,"stats":{"get_bytes":5698,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":31,"series":1,"stats":{"get_bytes":5681,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":32,"series":1,"stats":{"get_bytes":5804,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":33,"series":1,"stats":{"get_bytes":5810,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":34,"series":1,"stats":{"get_bytes":5665,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":35,"series":1,"stats":{"get_bytes":5665,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":36,"series":1,"stats":{"get_bytes":5706,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":37,"series":1,"stats":{"get_bytes":5705,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":38,"series":1,"stats":{"get_bytes":5793,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":39,"series":1,"stats":{"get_bytes":5706,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":40,"series":1,"stats":{"get_bytes":5706,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":41,"series":1,"stats":{"get_bytes":5747,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":42,"series":1,"stats":{"get_bytes":5747,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":43,"series":1,"stats":{"get_bytes":5837,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":44,"series":1,"stats":{"get_bytes":5591,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":45,"series":1,"stats":{"get_bytes":5924,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":46,"series":1,"stats":{"get_bytes":5780,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":47,"series":1,"stats":{"get_bytes":5755,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":48,"series":1,"stats":{"get_bytes":5583,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":49,"series":1,"stats":{"get_bytes":5673,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":50,"series":1,"stats":{"get_bytes":5678,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":51,"series":1,"stats":{"get_bytes":5640,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":52,"series":1,"stats":{"get_bytes":5657,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":53,"series":1,"stats":{"get_bytes":5616,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":54,"series":1,"stats":{"get_bytes":5802,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":55,"series":1,"stats":{"get_bytes":5884,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":56,"series":1,"stats":{"get_bytes":5747,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":57,"series":1,"stats":{"get_bytes":5630,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":58,"series":1,"stats":{"get_bytes":5739,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":59,"series":1,"stats":{"get_bytes":5583,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":60,"series":1,"stats":{"get_bytes":5542,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":61,"series":1,"stats":{"get_bytes":5739,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":62,"series":1,"stats":{"get_bytes":5493,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":63,"series":1,"stats":{"get_bytes":5632,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":64,"series":1,"stats":{"get_bytes":5550,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":65,"series":1,"stats":{"get_bytes":5680,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":66,"series":1,"stats":{"get_bytes":5714,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":67,"series":1,"stats":{"get_bytes":5624,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":68,"series":1,"stats":{"get_bytes":5583,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":69,"series":1,"stats":{"get_bytes":5739,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":70,"series":1,"stats":{"get_bytes":5862,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":71,"series":1,"stats":{"get_bytes":5550,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":72,"series":1,"stats":{"get_bytes":5780,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":73,"series":1,"stats":{"get_bytes":5575,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":74,"series":1,"stats":{"get_bytes":5640,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":75,"series":1,"stats":{"get_bytes":5932,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":76,"series":1,"stats":{"get_bytes":5811,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":77,"series":1,"stats":{"get_bytes":5624,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":78,"series":1,"stats":{"get_bytes":5534,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":79,"series":1,"stats":{"get_bytes":5762,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":80,"series":1,"stats":{"get_bytes":5755,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":81,"series":1,"stats":{"get_bytes":5719,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":82,"series":1,"stats":{"get_bytes":5583,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":83,"series":1,"stats":{"get_bytes":5542,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":84,"series":1,"stats":{"get_bytes":5811,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":85,"series":1,"stats":{"get_bytes":5698,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":86,"series":1,"stats":{"get_bytes":5591,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":87,"series":1,"stats":{"get_bytes":5837,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":88,"series":1,"stats":{"get_bytes":5624,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":89,"series":1,"stats":{"get_bytes":5616,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":90,"series":1,"stats":{"get_bytes":5640,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":91,"series":1,"stats":{"get_bytes":5747,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":92,"series":1,"stats":{"get_bytes":5706,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":93,"series":1,"stats":{"get_bytes":5727,"get_count":6,"put_bytes":0,"put_count":0}} +{"i":94,"series":1,"stats":{"get_bytes":5657,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":95,"series":1,"stats":{"get_bytes":5574,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":96,"series":1,"stats":{"get_bytes":5665,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":97,"series":1,"stats":{"get_bytes":5501,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":98,"series":1,"stats":{"get_bytes":5681,"get_count":5,"put_bytes":0,"put_count":0}} +{"i":99,"series":1,"stats":{"get_bytes":5672,"get_count":6,"put_bytes":0,"put_count":0}} diff --git a/actors/evm/tests/measurements/mapping_read_n1.png b/actors/evm/tests/measurements/mapping_read_n1.png new file mode 100644 index 000000000..6997ca60b Binary files /dev/null and b/actors/evm/tests/measurements/mapping_read_n1.png differ diff --git a/actors/evm/tests/measurements/mapping_read_n100.jsonline b/actors/evm/tests/measurements/mapping_read_n100.jsonline new file mode 100644 index 000000000..aebd44e18 --- /dev/null +++ b/actors/evm/tests/measurements/mapping_read_n100.jsonline @@ -0,0 +1,100 @@ +{"i":0,"series":1,"stats":{"get_bytes":93324,"get_count":156,"put_bytes":0,"put_count":0}} +{"i":1,"series":1,"stats":{"get_bytes":93296,"get_count":158,"put_bytes":0,"put_count":0}} +{"i":2,"series":1,"stats":{"get_bytes":89571,"get_count":150,"put_bytes":0,"put_count":0}} +{"i":3,"series":1,"stats":{"get_bytes":88146,"get_count":150,"put_bytes":0,"put_count":0}} +{"i":4,"series":1,"stats":{"get_bytes":93074,"get_count":152,"put_bytes":0,"put_count":0}} +{"i":5,"series":1,"stats":{"get_bytes":93956,"get_count":165,"put_bytes":0,"put_count":0}} +{"i":6,"series":1,"stats":{"get_bytes":87039,"get_count":144,"put_bytes":0,"put_count":0}} +{"i":7,"series":1,"stats":{"get_bytes":91319,"get_count":147,"put_bytes":0,"put_count":0}} +{"i":8,"series":1,"stats":{"get_bytes":95980,"get_count":151,"put_bytes":0,"put_count":0}} +{"i":9,"series":1,"stats":{"get_bytes":92377,"get_count":157,"put_bytes":0,"put_count":0}} +{"i":10,"series":1,"stats":{"get_bytes":93981,"get_count":159,"put_bytes":0,"put_count":0}} +{"i":11,"series":1,"stats":{"get_bytes":95872,"get_count":163,"put_bytes":0,"put_count":0}} +{"i":12,"series":1,"stats":{"get_bytes":96221,"get_count":160,"put_bytes":0,"put_count":0}} +{"i":13,"series":1,"stats":{"get_bytes":90859,"get_count":152,"put_bytes":0,"put_count":0}} +{"i":14,"series":1,"stats":{"get_bytes":91957,"get_count":158,"put_bytes":0,"put_count":0}} +{"i":15,"series":1,"stats":{"get_bytes":92346,"get_count":154,"put_bytes":0,"put_count":0}} +{"i":16,"series":1,"stats":{"get_bytes":92564,"get_count":158,"put_bytes":0,"put_count":0}} +{"i":17,"series":1,"stats":{"get_bytes":91418,"get_count":155,"put_bytes":0,"put_count":0}} +{"i":18,"series":1,"stats":{"get_bytes":87187,"get_count":155,"put_bytes":0,"put_count":0}} +{"i":19,"series":1,"stats":{"get_bytes":93368,"get_count":156,"put_bytes":0,"put_count":0}} +{"i":20,"series":1,"stats":{"get_bytes":87329,"get_count":153,"put_bytes":0,"put_count":0}} +{"i":21,"series":1,"stats":{"get_bytes":89857,"get_count":151,"put_bytes":0,"put_count":0}} +{"i":22,"series":1,"stats":{"get_bytes":87321,"get_count":151,"put_bytes":0,"put_count":0}} +{"i":23,"series":1,"stats":{"get_bytes":95078,"get_count":156,"put_bytes":0,"put_count":0}} +{"i":24,"series":1,"stats":{"get_bytes":94204,"get_count":163,"put_bytes":0,"put_count":0}} +{"i":25,"series":1,"stats":{"get_bytes":91756,"get_count":160,"put_bytes":0,"put_count":0}} +{"i":26,"series":1,"stats":{"get_bytes":90715,"get_count":161,"put_bytes":0,"put_count":0}} +{"i":27,"series":1,"stats":{"get_bytes":90415,"get_count":146,"put_bytes":0,"put_count":0}} +{"i":28,"series":1,"stats":{"get_bytes":91584,"get_count":162,"put_bytes":0,"put_count":0}} +{"i":29,"series":1,"stats":{"get_bytes":90117,"get_count":153,"put_bytes":0,"put_count":0}} +{"i":30,"series":1,"stats":{"get_bytes":94268,"get_count":162,"put_bytes":0,"put_count":0}} +{"i":31,"series":1,"stats":{"get_bytes":87610,"get_count":149,"put_bytes":0,"put_count":0}} +{"i":32,"series":1,"stats":{"get_bytes":94511,"get_count":155,"put_bytes":0,"put_count":0}} +{"i":33,"series":1,"stats":{"get_bytes":90730,"get_count":161,"put_bytes":0,"put_count":0}} +{"i":34,"series":1,"stats":{"get_bytes":92060,"get_count":155,"put_bytes":0,"put_count":0}} +{"i":35,"series":1,"stats":{"get_bytes":89444,"get_count":151,"put_bytes":0,"put_count":0}} +{"i":36,"series":1,"stats":{"get_bytes":90661,"get_count":149,"put_bytes":0,"put_count":0}} +{"i":37,"series":1,"stats":{"get_bytes":91747,"get_count":148,"put_bytes":0,"put_count":0}} +{"i":38,"series":1,"stats":{"get_bytes":91977,"get_count":154,"put_bytes":0,"put_count":0}} +{"i":39,"series":1,"stats":{"get_bytes":87624,"get_count":146,"put_bytes":0,"put_count":0}} +{"i":40,"series":1,"stats":{"get_bytes":91208,"get_count":153,"put_bytes":0,"put_count":0}} +{"i":41,"series":1,"stats":{"get_bytes":90622,"get_count":158,"put_bytes":0,"put_count":0}} +{"i":42,"series":1,"stats":{"get_bytes":91866,"get_count":160,"put_bytes":0,"put_count":0}} +{"i":43,"series":1,"stats":{"get_bytes":90449,"get_count":155,"put_bytes":0,"put_count":0}} +{"i":44,"series":1,"stats":{"get_bytes":93772,"get_count":153,"put_bytes":0,"put_count":0}} +{"i":45,"series":1,"stats":{"get_bytes":93255,"get_count":156,"put_bytes":0,"put_count":0}} +{"i":46,"series":1,"stats":{"get_bytes":93875,"get_count":165,"put_bytes":0,"put_count":0}} +{"i":47,"series":1,"stats":{"get_bytes":92998,"get_count":161,"put_bytes":0,"put_count":0}} +{"i":48,"series":1,"stats":{"get_bytes":94791,"get_count":152,"put_bytes":0,"put_count":0}} +{"i":49,"series":1,"stats":{"get_bytes":92610,"get_count":161,"put_bytes":0,"put_count":0}} +{"i":50,"series":1,"stats":{"get_bytes":88148,"get_count":146,"put_bytes":0,"put_count":0}} +{"i":51,"series":1,"stats":{"get_bytes":89436,"get_count":145,"put_bytes":0,"put_count":0}} +{"i":52,"series":1,"stats":{"get_bytes":93139,"get_count":159,"put_bytes":0,"put_count":0}} +{"i":53,"series":1,"stats":{"get_bytes":89083,"get_count":143,"put_bytes":0,"put_count":0}} +{"i":54,"series":1,"stats":{"get_bytes":92932,"get_count":156,"put_bytes":0,"put_count":0}} +{"i":55,"series":1,"stats":{"get_bytes":88831,"get_count":139,"put_bytes":0,"put_count":0}} +{"i":56,"series":1,"stats":{"get_bytes":89837,"get_count":152,"put_bytes":0,"put_count":0}} +{"i":57,"series":1,"stats":{"get_bytes":92780,"get_count":148,"put_bytes":0,"put_count":0}} +{"i":58,"series":1,"stats":{"get_bytes":91636,"get_count":157,"put_bytes":0,"put_count":0}} +{"i":59,"series":1,"stats":{"get_bytes":93875,"get_count":159,"put_bytes":0,"put_count":0}} +{"i":60,"series":1,"stats":{"get_bytes":91090,"get_count":148,"put_bytes":0,"put_count":0}} +{"i":61,"series":1,"stats":{"get_bytes":94673,"get_count":154,"put_bytes":0,"put_count":0}} +{"i":62,"series":1,"stats":{"get_bytes":87996,"get_count":154,"put_bytes":0,"put_count":0}} +{"i":63,"series":1,"stats":{"get_bytes":94095,"get_count":157,"put_bytes":0,"put_count":0}} +{"i":64,"series":1,"stats":{"get_bytes":93733,"get_count":153,"put_bytes":0,"put_count":0}} +{"i":65,"series":1,"stats":{"get_bytes":88072,"get_count":152,"put_bytes":0,"put_count":0}} +{"i":66,"series":1,"stats":{"get_bytes":92796,"get_count":157,"put_bytes":0,"put_count":0}} +{"i":67,"series":1,"stats":{"get_bytes":92716,"get_count":154,"put_bytes":0,"put_count":0}} +{"i":68,"series":1,"stats":{"get_bytes":93631,"get_count":156,"put_bytes":0,"put_count":0}} +{"i":69,"series":1,"stats":{"get_bytes":92175,"get_count":158,"put_bytes":0,"put_count":0}} +{"i":70,"series":1,"stats":{"get_bytes":94944,"get_count":161,"put_bytes":0,"put_count":0}} +{"i":71,"series":1,"stats":{"get_bytes":89895,"get_count":153,"put_bytes":0,"put_count":0}} +{"i":72,"series":1,"stats":{"get_bytes":90958,"get_count":157,"put_bytes":0,"put_count":0}} +{"i":73,"series":1,"stats":{"get_bytes":90525,"get_count":148,"put_bytes":0,"put_count":0}} +{"i":74,"series":1,"stats":{"get_bytes":89054,"get_count":152,"put_bytes":0,"put_count":0}} +{"i":75,"series":1,"stats":{"get_bytes":94682,"get_count":159,"put_bytes":0,"put_count":0}} +{"i":76,"series":1,"stats":{"get_bytes":92276,"get_count":156,"put_bytes":0,"put_count":0}} +{"i":77,"series":1,"stats":{"get_bytes":90372,"get_count":147,"put_bytes":0,"put_count":0}} +{"i":78,"series":1,"stats":{"get_bytes":91124,"get_count":149,"put_bytes":0,"put_count":0}} +{"i":79,"series":1,"stats":{"get_bytes":91462,"get_count":162,"put_bytes":0,"put_count":0}} +{"i":80,"series":1,"stats":{"get_bytes":91956,"get_count":154,"put_bytes":0,"put_count":0}} +{"i":81,"series":1,"stats":{"get_bytes":94459,"get_count":162,"put_bytes":0,"put_count":0}} +{"i":82,"series":1,"stats":{"get_bytes":91339,"get_count":156,"put_bytes":0,"put_count":0}} +{"i":83,"series":1,"stats":{"get_bytes":87572,"get_count":142,"put_bytes":0,"put_count":0}} +{"i":84,"series":1,"stats":{"get_bytes":95330,"get_count":156,"put_bytes":0,"put_count":0}} +{"i":85,"series":1,"stats":{"get_bytes":91173,"get_count":145,"put_bytes":0,"put_count":0}} +{"i":86,"series":1,"stats":{"get_bytes":92632,"get_count":151,"put_bytes":0,"put_count":0}} +{"i":87,"series":1,"stats":{"get_bytes":90743,"get_count":152,"put_bytes":0,"put_count":0}} +{"i":88,"series":1,"stats":{"get_bytes":91217,"get_count":160,"put_bytes":0,"put_count":0}} +{"i":89,"series":1,"stats":{"get_bytes":94371,"get_count":162,"put_bytes":0,"put_count":0}} +{"i":90,"series":1,"stats":{"get_bytes":96400,"get_count":160,"put_bytes":0,"put_count":0}} +{"i":91,"series":1,"stats":{"get_bytes":90713,"get_count":157,"put_bytes":0,"put_count":0}} +{"i":92,"series":1,"stats":{"get_bytes":96126,"get_count":159,"put_bytes":0,"put_count":0}} +{"i":93,"series":1,"stats":{"get_bytes":91255,"get_count":163,"put_bytes":0,"put_count":0}} +{"i":94,"series":1,"stats":{"get_bytes":92101,"get_count":151,"put_bytes":0,"put_count":0}} +{"i":95,"series":1,"stats":{"get_bytes":92698,"get_count":160,"put_bytes":0,"put_count":0}} +{"i":96,"series":1,"stats":{"get_bytes":89585,"get_count":148,"put_bytes":0,"put_count":0}} +{"i":97,"series":1,"stats":{"get_bytes":90969,"get_count":159,"put_bytes":0,"put_count":0}} +{"i":98,"series":1,"stats":{"get_bytes":89814,"get_count":154,"put_bytes":0,"put_count":0}} +{"i":99,"series":1,"stats":{"get_bytes":91850,"get_count":153,"put_bytes":0,"put_count":0}} diff --git a/actors/evm/tests/measurements/mapping_read_n100.png b/actors/evm/tests/measurements/mapping_read_n100.png new file mode 100644 index 000000000..91de2c30a Binary files /dev/null and b/actors/evm/tests/measurements/mapping_read_n100.png differ diff --git a/actors/evm/tests/measurements/storage-footprint.plt b/actors/evm/tests/measurements/storage-footprint.plt new file mode 100644 index 000000000..b2a7bafe7 --- /dev/null +++ b/actors/evm/tests/measurements/storage-footprint.plt @@ -0,0 +1,20 @@ +set term png size 1200,800; +set output fileout; +set y2tics; +set ytics nomirror; +set ylabel "Bytes"; +set y2label "Count"; +set key outside; + +# Show two plots in 2 rows, 1 column; +set multiplot layout 2, 1 ; + +set title scenario . ": GET Stats"; +plot for [i=0:*] filein index i using 2:4 with lines axis x1y1 title sprintf("GET bytes (%d)", i+1), \ + for [i=0:*] filein index i using 2:3 with points axis x1y2 title sprintf("GET count (%d)", i+1) + +set title scenario . ": PUT Stats"; +plot for [i=0:*] filein index i using 2:6 with lines axis x1y1 title sprintf("PUT bytes (%d)", i+1), \ + for [i=0:*] filein index i using 2:5 with points axis x1y2 title sprintf("PUT count (%d)", i+1) + +unset multiplot diff --git a/actors/evm/tests/measurements/storage-footprint.sh b/actors/evm/tests/measurements/storage-footprint.sh new file mode 100755 index 000000000..855f7399f --- /dev/null +++ b/actors/evm/tests/measurements/storage-footprint.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + +set -e + +# Name of the scenario to plot. +NAME=$1 + +if [ -z "$NAME" ]; then + echo "usage: storage-footprint.sh " + exit 1 +fi + +# Separate the two series (the current max) with newlines for gnuplot +# Ignoring warnings from gnuplot because sometimes we only have 1 series. + +rm -f $NAME.dat + +for S in 1 2; do + cat $NAME.jsonline \ + | jq -r "select(.series == $S) | [.series, .i, .stats.get_count, .stats.get_bytes, .stats.put_count, .stats.put_bytes] | @tsv" \ + >> $NAME.dat + echo $'\n' >> $NAME.dat +done + +gnuplot \ + -e "scenario='$(echo $NAME | tr _ - )'" \ + -e "filein='$NAME.dat'" \ + -e "fileout='$NAME.png'" \ + storage-footprint.plt \ + 2>/dev/null + +rm $NAME.dat diff --git a/actors/evm/tests/misc.rs b/actors/evm/tests/misc.rs new file mode 100644 index 000000000..76e3854ee --- /dev/null +++ b/actors/evm/tests/misc.rs @@ -0,0 +1,377 @@ +mod asm; +mod util; + +use cid::Cid; +use fil_actors_evm_shared::address::EthAddress; +use fil_actors_evm_shared::uints::U256; +use fvm_ipld_encoding::DAG_CBOR; +use fvm_shared::chainid::ChainID; +use fvm_shared::{address::Address, econ::TokenAmount}; +use multihash::Multihash; + +#[test] +fn test_timestamp() { + let contract = asm::new_contract( + "timestamp", + "", + r#" +timestamp +push1 0x00 +mstore +push1 0x20 +push1 0x00 +return +"#, + ) + .unwrap(); + + let mut rt = util::construct_and_verify(contract); + rt.tipset_timestamp = 123; + let result = util::invoke_contract(&mut rt, &[]); + assert_eq!(U256::from_big_endian(&result), U256::from(123)); +} + +#[test] +fn test_blockhash() { + let contract = asm::new_contract( + "blockhash", + "", + r#" +push2 0xffff +blockhash +push1 0x00 +mstore +push1 0x20 +push1 0x00 +return +"#, + ) + .unwrap(); + + let mut rt = util::construct_and_verify(contract); + + rt.tipset_cids = (0..(0xffff + 512)) + .map(|i| { + Cid::new_v1(DAG_CBOR, Multihash::wrap(0, format!("block-{:026}", i).as_ref()).unwrap()) + }) + .collect(); + + rt.epoch = 0xffff + 2; + let result = util::invoke_contract(&mut rt, &[]); + assert_eq!( + String::from_utf8_lossy(&result.to_vec()), + String::from_utf8_lossy(rt.tipset_cids[0xffff].hash().digest()) + ); + + rt.epoch = 0xffff + 256; + let result = util::invoke_contract(&mut rt, &[]); + assert_eq!( + String::from_utf8_lossy(&result.to_vec()), + String::from_utf8_lossy(rt.tipset_cids[0xffff].hash().digest()) + ); + + rt.epoch = 0xffff; + let result = util::invoke_contract(&mut rt, &[]); + assert_eq!(&result, &[0u8; 32]); + + rt.epoch = 0xffff - 1; + let result = util::invoke_contract(&mut rt, &[]); + assert_eq!(&result, &[0u8; 32]); + + rt.epoch = 0xffff + 257; + let result = util::invoke_contract(&mut rt, &[]); + assert_eq!(&result, &[0u8; 32]); +} + +#[test] +fn test_chainid() { + let contract = asm::new_contract( + "chainid", + "", + r#" +chainid +push1 0x00 +mstore +push1 0x20 +push1 0x00 +return +"#, + ) + .unwrap(); + + let mut rt = util::construct_and_verify(contract); + rt.chain_id = ChainID::from(1989); + let result = util::invoke_contract(&mut rt, &[]); + assert_eq!(U256::from_big_endian(&result), U256::from(1989)); +} + +#[test] +fn test_gas_limit() { + let contract = asm::new_contract( + "gaslimit", + "", + r#" +gaslimit +push1 0x00 +mstore +push1 0x20 +push1 0x00 +return +"#, + ) + .unwrap(); + + let mut rt = util::construct_and_verify(contract); + let result = util::invoke_contract(&mut rt, &[]); + assert_eq!(U256::from_big_endian(&result), U256::from(10_000_000_000u64)); +} + +#[test] +fn test_gas_price() { + let contract = asm::new_contract( + "timestamp", + "", + r#" +gasprice +push1 0x00 +mstore +push1 0x20 +push1 0x00 +return +"#, + ) + .unwrap(); + + let mut rt = util::construct_and_verify(contract); + rt.base_fee = TokenAmount::from_atto(123); + rt.gas_premium = TokenAmount::from_atto(345); + let result = util::invoke_contract(&mut rt, &[]); + assert_eq!(U256::from_big_endian(&result), U256::from(123 + 345)); +} + +#[test] +fn test_balance() { + let contract = asm::new_contract( + "balance", + "", + r#" +push1 0x20 +push1 0x00 +push1 0x00 +calldatacopy +push1 0x00 +mload +balance +push1 0x00 +mstore +push1 0x20 +push1 0x00 +return +"#, + ) + .unwrap(); + + let mut rt = util::construct_and_verify(contract); + rt.actor_balances.insert(100, TokenAmount::from_atto(123)); + let mut input_data = vec![0u8; 32]; + input_data[12] = 0xff; + input_data[31] = 0x64; + let result = util::invoke_contract(&mut rt, &input_data); + assert_eq!(U256::from_big_endian(&result), U256::from(123)); +} + +#[test] +fn test_balance_bogus() { + let contract = asm::new_contract( + "balance", + "", + r#" +push1 0x20 +push1 0x00 +push1 0x00 +calldatacopy +push1 0x00 +mload +balance +push1 0x00 +mstore +push1 0x20 +push1 0x00 +return +"#, + ) + .unwrap(); + + let mut rt = util::construct_and_verify(contract); + let mut input_data = vec![0u8; 32]; + input_data[31] = 123; + let result = util::invoke_contract(&mut rt, &input_data); + assert_eq!(U256::from_big_endian(&result), U256::from(0)); +} + +#[test] +fn test_balance0() { + let contract = asm::new_contract( + "balance", + "", + r#" +push1 0x00 +balance +push1 0x00 +mstore +push1 0x20 +push1 0x00 +return +"#, + ) + .unwrap(); + + let mut rt = util::construct_and_verify(contract); + let result = util::invoke_contract(&mut rt, &[]); + assert_eq!(U256::from_big_endian(&result), U256::from(0)); +} + +#[test] +fn test_gas() { + let contract = asm::new_contract( + "gas", + "", + r#" +gas +push1 0x00 +mstore +push1 0x20 +push1 0x00 +return +"#, + ) + .unwrap(); + + let mut rt = util::construct_and_verify(contract); + rt.expect_gas_available(123); + let result = util::invoke_contract(&mut rt, &[]); + assert_eq!(U256::from_big_endian(&result), U256::from(123)); +} + +#[test] +fn test_address() { + let contract = asm::new_contract( + "gas", + "", + r#" +push1 0x00 +address +push1 0x00 +mstore +push1 0x20 +push1 0x00 +return +"#, + ) + .unwrap(); + + let mut rt = util::construct_and_verify(contract); + let result = util::invoke_contract(&mut rt, &[]); + let eth_address = &result[12..]; + // Make sure we get an actual eth address, not an embedded ID address. + assert_eq!(ð_address, &util::CONTRACT_ADDRESS); +} + +#[test] +fn test_caller_id() { + let contract = asm::new_contract( + "gas", + "", + r#" +push1 0x00 +caller +push1 0x00 +mstore +push1 0x20 +push1 0x00 +return +"#, + ) + .unwrap(); + + let mut rt = util::construct_and_verify(contract); + let result = util::invoke_contract(&mut rt, &[]); + let eth_address = &result[12..]; + // The caller's address should be the init actor in this case. + assert_eq!(ð_address, &EthAddress::from_id(1).0); +} + +#[test] +fn test_caller_eth() { + let contract = asm::new_contract( + "gas", + "", + r#" +push1 0x00 +caller +push1 0x00 +mstore +push1 0x20 +push1 0x00 +return +"#, + ) + .unwrap(); + + let mut rt = util::construct_and_verify(contract); + // set the _id_ address here (ensures we resolve it correctly internally). + rt.caller = Address::new_id(0); + let result = util::invoke_contract(&mut rt, &[]); + let eth_address = &result[12..]; + // Make sure we prefer the eth address, if we have one. + assert_eq!(eth_address, util::CONTRACT_ADDRESS); +} + +#[test] +fn test_origin_id() { + let contract = asm::new_contract( + "gas", + "", + r#" +push1 0x00 +origin +push1 0x00 +mstore +push1 0x20 +push1 0x00 +return +"#, + ) + .unwrap(); + + let mut rt = util::construct_and_verify(contract); + rt.origin = Address::new_id(10); + let result = util::invoke_contract(&mut rt, &[]); + let eth_address = &result[12..]; + // Make sure we prefer the eth address, if we have one. + assert_eq!(eth_address, &EthAddress::from_id(10).0); +} + +#[test] +fn test_origin_eth() { + let contract = asm::new_contract( + "gas", + "", + r#" +push1 0x00 +origin +push1 0x00 +mstore +push1 0x20 +push1 0x00 +return +"#, + ) + .unwrap(); + + let mut rt = util::construct_and_verify(contract); + let result = util::invoke_contract(&mut rt, &[]); + let eth_address = &result[12..]; + // Make sure we prefer the eth address, if we have one. + assert_eq!(eth_address, util::CONTRACT_ADDRESS); +} diff --git a/actors/evm/tests/precompile.rs b/actors/evm/tests/precompile.rs new file mode 100644 index 000000000..8ce9b72ca --- /dev/null +++ b/actors/evm/tests/precompile.rs @@ -0,0 +1,302 @@ +mod asm; + +use fil_actors_evm_shared::{address::EthAddress, uints::U256}; +use fil_actors_runtime::{ + test_utils::{new_bls_addr, MockRuntime}, + EAM_ACTOR_ID, +}; +use fvm_shared::{address::Address as FILAddress, econ::TokenAmount, error::ExitCode, METHOD_SEND}; + +mod util; + +use util::{id_to_vec, NativePrecompile, PrecompileExit, PrecompileTest}; + +#[allow(dead_code)] +pub fn magic_precompile_contract() -> Vec { + let init = r#" +"#; + + let body = r#" +push16 0x666F6F206261722062617A20626F7879 # foo bar baz boxy +push2 0x0100 # offset of input data +mstore # store value at offset + +%push(sha256_hash) +jump # call hash, output written to 0x0200 + +sha256_hash: +jumpdest +push1 0x20 # out size (32 bytes) +push2 0x0200 # out offset +push1 0x10 # in size (16 bytes) +push2 0x0110 # in offset +push1 0x00 # _value +push1 0x02 # dst (0x02 is keccak-256) +push1 0x00 # _gas +call +push1 0x20 +push2 0x0200 +return +"#; + + asm::new_contract("magic-precompile", init, body).unwrap() +} + +#[test] +fn test_precompile_hash() { + let contract = magic_precompile_contract(); + let mut rt = util::construct_and_verify(contract); + + // invoke contract + let contract_params = vec![0u8; 32]; + + rt.expect_gas_available(10_000_000_000u64); + let result = util::invoke_contract(&mut rt, &contract_params); + let expected = + hex_literal::hex!("ace8597929092c14bd028ede7b07727875788c7e130278b5afed41940d965aba"); + assert_eq!( + U256::from_big_endian(&result), + U256::from(expected), + "\n{}\n{}", + hex::encode(&*result), + hex::encode(expected) + ); +} + +fn tester_bytecode() -> Vec { + let (init, body) = util::PrecompileTest::test_runner_assembly(); + asm::new_contract("precompile-tester", &init, &body).unwrap() +} + +fn resolve_address_contract() -> Vec { + let init = ""; + let body = r#" + +# get call payload size +calldatasize +# store payload to mem 0x00 +push1 0x00 +push1 0x00 +calldatacopy + +# out size +# out off +push1 0x20 +push1 0xA0 + +# in size +# in off +calldatasize +push1 0x00 + +# value +push1 0x00 + +# dst (resolve_address precompile) +push20 0xfe00000000000000000000000000000000000001 + +# gas +push1 0x00 + +call + +# write exit code memory +push1 0x00 # offset +mstore8 + +returndatasize +push1 0x00 # offset +push1 0x01 # dest offset +returndatacopy + +returndatasize +push1 0x01 +add +push1 0x00 +return +"#; + asm::new_contract("native_precompiles", init, body).unwrap() +} + +#[test] +fn test_native_lookup_delegated_address() { + let mut rt = util::construct_and_verify(tester_bytecode()); + + // f0 10101 is an EVM actor + let evm_target = FILAddress::new_id(10101); + let evm_del = EthAddress(util::CONTRACT_ADDRESS).try_into().unwrap(); + rt.set_delegated_address(evm_target.id().unwrap(), evm_del); + + // f0 10111 is an actor with a non-evm delegate address + let unknown_target = FILAddress::new_id(10111); + let unknown_del = FILAddress::new_delegated(1234, "foobarboxy".as_bytes()).unwrap(); + rt.set_delegated_address(unknown_target.id().unwrap(), unknown_del); + + fn test_lookup_address(rt: &mut MockRuntime, id: FILAddress, expected: Vec) { + let test = PrecompileTest { + precompile_address: NativePrecompile::LookupDelegatedAddress.eth_address(), + output_size: 32, + expected_exit_code: PrecompileExit::Success, + gas_avaliable: 10_000_000_000, + call_op: util::PrecompileCallOpcode::Call(0), + expected_return: expected, + input: id_to_vec(&id), + }; + + test.run_test(rt); + } + + test_lookup_address(&mut rt, evm_target, evm_del.to_bytes()); + test_lookup_address(&mut rt, unknown_target, unknown_del.to_bytes()); + test_lookup_address(&mut rt, FILAddress::new_id(11111), Vec::new()); +} + +#[test] +fn test_resolve_delegated() { + let bytecode = resolve_address_contract(); + let mut rt = util::construct_and_verify(bytecode); + + // EVM actor + let evm_target = FILAddress::new_id(10101); + let evm_del = EthAddress(util::CONTRACT_ADDRESS).try_into().unwrap(); + rt.set_delegated_address(evm_target.id().unwrap(), evm_del); + + // Actor with a non-evm delegate address + let unknown_target = FILAddress::new_id(10111); + let unknown_del = FILAddress::new_delegated(1234, "foobarboxy".as_bytes()).unwrap(); + rt.set_delegated_address(unknown_target.id().unwrap(), unknown_del); + + // Non-bound f4 address + let unbound_del = FILAddress::new_delegated(0xffff, "foobarboxybeef".as_bytes()).unwrap(); + + // Actor with a secp address + let secp_target = FILAddress::new_id(10112); + let secp = { + let mut protocol = vec![1u8]; + let payload = [0xff; 20]; + protocol.extend_from_slice(&payload); + FILAddress::from_bytes(&protocol).unwrap() + }; + rt.add_id_address(secp, secp_target); + + // Actor with a bls address + let bls_target = FILAddress::new_id(10113); + let bls = new_bls_addr(123); + rt.add_id_address(bls, bls_target); + + fn test_resolve(rt: &mut MockRuntime, addr: FILAddress, expected: Vec) { + rt.expect_gas_available(10_000_000_000u64); + let input = addr.to_bytes(); + let result = util::invoke_contract(rt, &input); + rt.verify(); + assert_eq!(expected, &result[1..]); + assert_eq!(1, result[0]); + rt.reset(); + } + + test_resolve(&mut rt, evm_del, id_to_vec(&evm_target)); + test_resolve(&mut rt, unknown_del, id_to_vec(&unknown_target)); + test_resolve(&mut rt, secp, id_to_vec(&secp_target)); + test_resolve(&mut rt, bls, id_to_vec(&bls_target)); + // not found + test_resolve(&mut rt, unbound_del, vec![]); + + // invalid first param fails + rt.expect_gas_available(10_000_000_000u64); + let result = util::invoke_contract(&mut rt, &[0xff; 1]); + rt.verify(); + assert_eq!(&[0u8], result.as_slice()); + rt.reset(); + + // invalid second param fails + rt.expect_gas_available(10_000_000_000u64); + let input = { + // first word is len + let mut v = U256::from(5).to_bytes().to_vec(); + // then addr + v.extend_from_slice(&[0, 0, 0xff]); + v + }; + let result = util::invoke_contract(&mut rt, &input); + rt.verify(); + assert_eq!(&[0u8], result.as_slice()); + rt.reset(); +} + +#[test] +fn test_precompile_transfer() { + let (init, body) = util::PrecompileTest::test_runner_assembly(); + + let mut rt = + util::construct_and_verify(asm::new_contract("precompile-tester", &init, &body).unwrap()); + rt.set_balance(TokenAmount::from_atto(100)); + // test invalid precompile address + for (prefix, index) in [(0x00, 0xff), (0xfe, 0xff)] { + let addr = util::precompile_address(prefix, index); + let test = PrecompileTest { + precompile_address: addr, + output_size: 32, + expected_exit_code: PrecompileExit::Success, + gas_avaliable: 10_000_000_000, + call_op: util::PrecompileCallOpcode::Call(1), + input: vec![0xff; 32], + expected_return: vec![], + }; + let fil_addr = FILAddress::new_delegated(EAM_ACTOR_ID, addr.as_ref()).unwrap(); + rt.expect_send_simple( + fil_addr, + METHOD_SEND, + None, + TokenAmount::from_atto(1), + None, + ExitCode::OK, + ); + test.run_test(&mut rt); + } + assert_eq!(rt.get_balance(), TokenAmount::from_atto(98)); +} + +#[test] +fn test_precompile_transfer_nothing() { + let (init, body) = util::PrecompileTest::test_runner_assembly(); + + let mut rt = + util::construct_and_verify(asm::new_contract("precompile-tester", &init, &body).unwrap()); + rt.set_balance(TokenAmount::from_atto(100)); + // test invalid precompile address + for (prefix, index) in [(0x00, 0xff), (0xfe, 0xff), (0xfe, 0xef)] { + let addr = util::precompile_address(prefix, index); + let test = PrecompileTest { + precompile_address: addr, + output_size: 32, + expected_exit_code: PrecompileExit::Success, + gas_avaliable: 10_000_000_000, + call_op: util::PrecompileCallOpcode::Call(0), + input: vec![0xff; 32], + expected_return: vec![], + }; + test.run_test(&mut rt); + } + assert_eq!(rt.get_balance(), TokenAmount::from_atto(100)); +} + +#[test] +fn test_precompile_failure() { + let bytecode = resolve_address_contract(); + let mut rt = util::construct_and_verify(bytecode); + + // invalid input fails + rt.expect_gas_available(10_000_000_000u64); + let result = util::invoke_contract(&mut rt, &[0xff; 32]); + rt.verify(); + assert_eq!(&[0u8], result.as_slice()); + rt.reset(); + + // not found succeeds with empty + rt.expect_gas_available(10_000_000_000u64); + let input = FILAddress::new_delegated(111, b"foo").unwrap().to_bytes(); + let result = util::invoke_contract(&mut rt, &input); + rt.verify(); + assert_eq!(&[1u8], result.as_slice()); + rt.reset(); +} diff --git a/actors/evm/tests/revert.rs b/actors/evm/tests/revert.rs new file mode 100644 index 000000000..3f8b33221 --- /dev/null +++ b/actors/evm/tests/revert.rs @@ -0,0 +1,34 @@ +use fil_actor_evm as evm; +use fvm_ipld_encoding::{BytesSer, RawBytes}; + +mod asm; +mod util; + +#[test] +fn test_revert() { + let contract = asm::new_contract( + "naked-revert", + "", + r#" +%push(0xdeadbeef) +push1 0x00 +mstore +push1 0x04 +push1 0x1c # skip top 28 bytes +revert +"#, + ) + .unwrap(); + + let mut rt = util::construct_and_verify(contract); + rt.expect_validate_caller_any(); + + let result = rt.call::(evm::Method::InvokeContract as u64, None); + assert!(result.is_err()); + let mut e = result.unwrap_err(); + assert_eq!(e.exit_code(), evm::EVM_CONTRACT_REVERTED); + assert_eq!( + e.take_data().unwrap().data, + RawBytes::serialize(BytesSer(&[0xde, 0xad, 0xbe, 0xef])).unwrap().bytes() + ); +} diff --git a/actors/evm/tests/selfdestruct.rs b/actors/evm/tests/selfdestruct.rs new file mode 100644 index 000000000..9b7a5c0cc --- /dev/null +++ b/actors/evm/tests/selfdestruct.rs @@ -0,0 +1,143 @@ +use fil_actor_evm::{ + EvmContractActor, Method, ResurrectParams, State, Tombstone, EVM_CONTRACT_SELFDESTRUCT_FAILED, +}; +use fil_actors_evm_shared::{address::EthAddress, uints::U256}; +use fil_actors_runtime::{test_utils::*, EAM_ACTOR_ADDR, INIT_ACTOR_ADDR}; +use fvm_ipld_encoding::{ipld_block::IpldBlock, BytesSer, RawBytes}; +use fvm_shared::{ + address::Address, + econ::TokenAmount, + error::{ErrorNumber, ExitCode}, + sys::SendFlags, + MethodNum, METHOD_SEND, +}; +use num_traits::Zero; + +mod util; + +#[test] +fn test_selfdestruct() { + let bytecode = hex::decode(include_str!("contracts/selfdestruct.hex")).unwrap(); + + let contract = Address::new_id(100); + let beneficiary_id = 1001; + let beneficiary = Address::new_id(beneficiary_id); + + let token_amount = TokenAmount::from_whole(2); + + let mut rt = util::init_construct_and_verify(bytecode.clone(), |rt| { + rt.actor_code_cids.insert(contract, *EVM_ACTOR_CODE_ID); + rt.set_origin(contract); + rt.set_balance(token_amount.clone()); + }); + + let returnone_params = hex::decode("901717d1").unwrap(); + let selfdestruct_params = hex::decode("35f46994").unwrap(); + let resurrect_params = IpldBlock::serialize_cbor(&ResurrectParams { + creator: EthAddress([0; 20]), + initcode: RawBytes::new(bytecode), + }) + .unwrap(); + + rt.expect_send_simple(beneficiary, METHOD_SEND, None, token_amount, None, ExitCode::OK); + + assert!(util::invoke_contract(&mut rt, &selfdestruct_params).is_empty()); + let state: State = rt.get_state(); + assert_eq!(state.tombstone, Some(Tombstone { origin: 100, nonce: 0 })); + rt.verify(); + + // Calls still work. + assert_eq!( + U256::from_big_endian(&util::invoke_contract(&mut rt, &returnone_params)), + U256::ONE + ); + rt.verify(); + + // Try to resurrect during the same "epoch". This should be forbidden. + rt.set_caller(*EAM_ACTOR_CODE_ID, EAM_ACTOR_ADDR); + rt.expect_validate_caller_addr(vec![EAM_ACTOR_ADDR]); + assert_eq!( + rt.call::(Method::Resurrect as MethodNum, resurrect_params.clone()) + .unwrap_err() + .exit_code(), + ExitCode::USR_FORBIDDEN + ); + rt.verify(); + + rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); // doesn't really matter + + // Selfdestruct should be callable multiple times, and it shouldn't do anything (but move + // remaining funds, again). + rt.expect_validate_caller_any(); + rt.expect_send_simple(beneficiary, METHOD_SEND, None, TokenAmount::zero(), None, ExitCode::OK); + assert!(util::invoke_contract(&mut rt, &selfdestruct_params).is_empty()); + rt.verify(); + + // Ok, call from a different origin so that the tombstone prevents any calls. + rt.set_origin(beneficiary); + + // All calls should now do nothing (but still work). + assert!(util::invoke_contract(&mut rt, &returnone_params).is_empty()); + rt.verify(); + + // We should now be able to resurrect. + rt.set_caller(*EAM_ACTOR_CODE_ID, EAM_ACTOR_ADDR); + rt.expect_validate_caller_addr(vec![EAM_ACTOR_ADDR]); + rt.call::(Method::Resurrect as MethodNum, resurrect_params).unwrap(); + rt.verify(); + + // The tombstone should be gone! + let state: State = rt.get_state(); + assert_eq!(state.tombstone, None); + + rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); // doesn't really matter + + // And calls should work again. + assert_eq!( + U256::from_big_endian(&util::invoke_contract(&mut rt, &returnone_params)), + U256::ONE + ); + rt.verify(); +} + +#[test] +fn test_selfdestruct_missing_beneficiary() { + let bytecode = hex::decode(include_str!("contracts/selfdestruct.hex")).unwrap(); + + let contract = Address::new_id(100); + let beneficiary = Address::new_id(1001); + + let mut rt = util::init_construct_and_verify(bytecode, |rt| { + rt.actor_code_cids.insert(contract, *EVM_ACTOR_CODE_ID); + rt.set_origin(contract); + }); + + let solidity_params = hex::decode("35f46994").unwrap(); + rt.expect_validate_caller_any(); + rt.expect_send( + beneficiary, + METHOD_SEND, + None, + rt.get_balance(), + None, + SendFlags::default(), + None, + ExitCode::OK, // doesn't matter + Some(ErrorNumber::NotFound), + ); + + // It still works even if the beneficiary doesn't exist. + + assert_eq!( + rt.call::( + Method::InvokeContract as u64, + IpldBlock::serialize_cbor(&BytesSer(&solidity_params)).unwrap(), + ) + .expect_err("call should have failed") + .exit_code(), + EVM_CONTRACT_SELFDESTRUCT_FAILED, + ); + let state: State = rt.get_state(); + assert_eq!(state.tombstone, None); + rt.verify(); +} diff --git a/actors/evm/tests/storage_footprint.rs b/actors/evm/tests/storage_footprint.rs new file mode 100644 index 000000000..1d2078a8c --- /dev/null +++ b/actors/evm/tests/storage_footprint.rs @@ -0,0 +1,245 @@ +use std::fs::File; +use std::io::Write; +use std::sync::Arc; + +use ethers::contract::Lazy; +use ethers::prelude::abigen; +use ethers::providers::{MockProvider, Provider}; +use fil_actors_evm_shared::address::EthAddress; +use fvm_ipld_blockstore::tracking::BSStats as BlockstoreStats; +use fvm_shared::address::Address; +use fvm_shared::ActorID; + +mod env; + +use env::{TestContractCall, TestEnv}; +use serde_json::json; + +// Generate a statically typed interface for the contract. +abigen!(StorageFootprint, "./tests/contracts/StorageFootprint.abi"); + +// Alternatively we can generate the ABI code as follows: +// ``` +// ethers::prelude::Abigen::new("StorageFootprint", "./tests/contracts/StorageFootprint.abi") +// .unwrap() +// .generate() +// .unwrap() +// .write_to_file("./tests/storage_footprint_abi.rs") +// .unwrap(); +// ``` + +// The owner doesn't matter in these tests, so just using the same value that the other tests use, for everything. +const OWNER_ID: ActorID = 100; +const OWNER: Address = Address::new_id(OWNER_ID); + +static CONTRACT: Lazy>> = Lazy::new(|| { + // The owner of the contract is expected to be the 160 bit hash used on Ethereum. + // We're not going to use it during the tests. + let address = EthAddress::from_id(OWNER_ID); + let address = ethers::core::types::Address::from_slice(address.as_ref()); + // A dummy client that we don't intend to use to call the contract or send transactions. + let (client, _mock) = Provider::mocked(); + StorageFootprint::new(address, Arc::new(client)) +}); + +/// Create a fresh test environment. +fn new_footprint_env() -> TestEnv { + let mut env = TestEnv::new(OWNER); + env.deploy(include_str!("contracts/StorageFootprint.hex")); + env +} + +struct Measurements { + scenario: String, + values: Vec, +} + +impl Measurements { + pub fn new(scenario: String) -> Self { + Self { scenario, values: Vec::new() } + } + + pub fn record(&mut self, series: u8, i: u32, stats: BlockstoreStats) { + // Not merging `i` into `stats` in JSON so the iteration appear in the left. + let value = json!({ + "i": i, + "series": series, + "stats": { + "get_count": stats.r, + "get_bytes": stats.br, + "put_count": stats.w, + "put_bytes": stats.bw, + } + }); + self.values.push(value); + } + + pub fn export(self) -> Result<(), std::io::Error> { + let dir = std::env!("CARGO_MANIFEST_DIR"); + let path = format!("{}/tests/measurements/{}.jsonline", dir, self.scenario); + let mut output = File::create(path)?; + for value in self.values { + writeln!(output, "{}", value)?; + } + Ok(()) + } +} + +#[test] +fn basic() { + let mut env = new_footprint_env(); + let sum = env.call(CONTRACT.array_1_sum(0, 0)); + assert_eq!(sum, 0) +} + +/// Number of iterations to do in a scenario, ie. the number of observations along the X axis. +const NUM_ITER: u32 = 100; + +/// Measure the cost of pushing items into dynamic arrays. Run multiple scenarios +/// with different number of items pushed in one call. First do a number of iterations +/// with `array1`, then with `array2` to see if the former affects the latter. +#[test] +fn measure_array_push() { + // Number of items to push at the end of the array at a time. + for n in [1, 100] { + let mut env = new_footprint_env(); + let mut mts = Measurements::new(format!("array_push_n{}", n)); + for i in 0..NUM_ITER { + env.call(CONTRACT.array_1_push(n)); + mts.record(1, i, env.take_store_stats()); + } + for i in 0..NUM_ITER { + env.call(CONTRACT.array_2_push(n)); + mts.record(2, i, env.take_store_stats()); + } + mts.export().unwrap() + } +} + +/// Measure the cost of adding items to a mapping. Run multiple scenarios with different +/// number of items added at the same time. Add to `mapping1` first, then `mapping2`. +#[test] +fn measure_mapping_add() { + // Number of items to add to the mapping at a time + for n in [1, 100] { + let mut env = new_footprint_env(); + let mut mts = Measurements::new(format!("mapping_add_n{}", n)); + // In this case we always add new keys, never overwrite existing ones, to compare to + // the scenario where we were pushing to the end of arrays. + for i in 0..NUM_ITER { + env.call(CONTRACT.mapping_1_set(i * n, n, i)); + mts.record(1, i, env.take_store_stats()); + } + for i in 0..NUM_ITER { + env.call(CONTRACT.mapping_2_set(i * n, n, i)); + mts.record(2, i, env.take_store_stats()); + } + mts.export().unwrap() + } +} + +/// Measure the cost of overwriting existing mapping keys. +/// Fill a mapping with 1000 items, then overwrite 10 at a time. +/// Next, loop through them again but overwrite with the same value. +#[test] +fn measure_mapping_overwrite() { + let n = 10; + let mut env = new_footprint_env(); + let mut mts = Measurements::new("mapping_overwrite".into()); + env.call(CONTRACT.mapping_1_set(0, 1000, 1)); + env.clear_store_stats(); + + for s in [1, 2] { + for i in 0..NUM_ITER { + env.call(CONTRACT.mapping_1_set(i * n, n, 2)); + mts.record(s, i, env.take_store_stats()); + } + } + + mts.export().unwrap() +} + +/// Fill an array with 10,000 items, then read varying number of consecutive entries from it. +#[test] +fn measure_array_read() { + let mut env = new_footprint_env(); + env.call(CONTRACT.array_1_push(10000)); + + let sum = env.call(CONTRACT.array_1_sum(0, 10000)); + assert_eq!(sum, (1 + 10000) * 10000 / 2); + + env.clear_store_stats(); + + // Number of items to access from the array at a time. + for n in [1, 100] { + let mut mts = Measurements::new(format!("array_read_n{}", n)); + for i in 0..NUM_ITER { + env.call(CONTRACT.array_1_sum(i * n, n)); + mts.record(1, i, env.take_store_stats()); + } + mts.export().unwrap() + } +} + +/// Fill a mapping with 10,000 items, then read varying number of consecutive entries from it. +#[test] +fn measure_mapping_read() { + let mut env = new_footprint_env(); + env.call(CONTRACT.mapping_1_set(0, 10000, 1)); + + let sum = env.call(CONTRACT.mapping_1_sum(0, 10000)); + assert_eq!(sum, 10000); + + env.clear_store_stats(); + + // Number of items to access from the mapping at a time. + for n in [1, 100] { + let mut mts = Measurements::new(format!("mapping_read_n{}", n)); + for i in 0..NUM_ITER { + env.call(CONTRACT.mapping_1_sum(i * n, n)); + mts.record(1, i, env.take_store_stats()); + } + mts.export().unwrap() + } +} + +/// Meausre the cost of accessing one storage variable vs multiple. +#[test] +fn measure_inc_one_vs_all() { + let mut env = new_footprint_env(); + let mut mts = Measurements::new("inc_one_vs_all".into()); + + // Interleave incrementing one and all, otherwise there's really nothing else happening. + for i in 0..10 { + env.call(CONTRACT.inc_counter_1()); + mts.record(1, i, env.take_store_stats()); + env.call(CONTRACT.inc_counters()); + mts.record(2, i, env.take_store_stats()); + } + + mts.export().unwrap() +} + +/// Meausre the cost of incrementing a single counter after arrays and maps have already +/// been filled to some extent. +#[test] +fn measure_inc_after_fill() { + let mut mts = Measurements::new("inc_after_fill".into()); + + let mut go = |series, fill: Box TestContractCall<()>>| { + let mut env = new_footprint_env(); + // Then the single counter, so the read/put bytes already include the other non-zero values. + for i in 0..NUM_ITER { + env.call(fill(i)); + env.clear_store_stats(); + + env.call(CONTRACT.inc_counter_1()); + mts.record(series, i, env.take_store_stats()); + } + }; + + go(1, Box::new(|_| CONTRACT.array_1_push(10))); + go(2, Box::new(|i| CONTRACT.mapping_1_set(i * 10, 10, 1))); + + mts.export().unwrap() +} diff --git a/actors/evm/tests/util.rs b/actors/evm/tests/util.rs new file mode 100644 index 000000000..a157453e9 --- /dev/null +++ b/actors/evm/tests/util.rs @@ -0,0 +1,392 @@ +use cid::Cid; +use fil_actor_evm as evm; +use fil_actor_evm::State; +use fil_actors_evm_shared::address::EthAddress; +use fil_actors_evm_shared::uints::U256; +use fil_actors_runtime::runtime::Runtime; +use fil_actors_runtime::{ + test_utils::{self, *}, + EAM_ACTOR_ID, INIT_ACTOR_ADDR, +}; +use fvm_ipld_blockstore::Blockstore; +use fvm_ipld_encoding::ipld_block::IpldBlock; +use fvm_ipld_encoding::{BytesDe, BytesSer}; +use fvm_shared::{address::Address, IDENTITY_HASH, IPLD_RAW}; +use lazy_static::lazy_static; + +use std::fmt::Debug; + +#[allow(dead_code)] +pub fn construct_and_verify(initcode: Vec) -> MockRuntime { + init_construct_and_verify(initcode, |_| {}) +} + +pub const CONTRACT_ADDRESS: [u8; 20] = + hex_literal::hex!("FEEDFACECAFEBEEF000000000000000000000000"); + +#[allow(unused)] +pub const CONTRACT_ID: Address = Address::new_id(0); + +pub fn init_construct_and_verify( + initcode: Vec, + initrt: F, +) -> MockRuntime { + let mut rt = MockRuntime::default(); + + // enable logging to std + test_utils::init_logging().ok(); + + // construct EVM actor + rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); + rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); + initrt(&mut rt); + + // first actor created is 0 + rt.set_delegated_address(0, Address::new_delegated(EAM_ACTOR_ID, &CONTRACT_ADDRESS).unwrap()); + rt.set_address_actor_type(Address::new_id(0), *EVM_ACTOR_CODE_ID); + + let params = evm::ConstructorParams { + creator: EthAddress::from_id(fil_actors_runtime::EAM_ACTOR_ADDR.id().unwrap()), + initcode: initcode.into(), + }; + + assert!(rt + .call::( + evm::Method::Constructor as u64, + IpldBlock::serialize_cbor(¶ms).unwrap(), + ) + .unwrap() + .is_none()); + let evm_st: State = rt.state().unwrap(); + let evm_code = rt.store.get(&evm_st.bytecode).unwrap().unwrap(); + log::trace!("bytecode constructed: {}", hex::encode(evm_code)); + rt.verify(); + + rt +} + +#[allow(dead_code)] +pub fn invoke_contract(rt: &mut MockRuntime, input_data: &[u8]) -> Vec { + rt.expect_validate_caller_any(); + let BytesDe(res) = rt + .call::( + evm::Method::InvokeContract as u64, + IpldBlock::serialize_cbor(&BytesSer(input_data)).unwrap(), + ) + .unwrap() + .unwrap() + .deserialize() + .unwrap(); + res +} + +#[allow(dead_code)] +// silly to have the full word for a single byte but... +pub fn dispatch_num_word(method_num: u8) -> [u8; 32] { + let mut word = [0u8; 32]; + word[3] = method_num; + word +} + +#[allow(dead_code)] +pub fn id_to_vec(src: &Address) -> Vec { + U256::from(src.id().unwrap()).to_bytes().to_vec() +} + +lazy_static! { + pub static ref DUMMY_ACTOR_CODE_ID: Cid = + Cid::new_v1(IPLD_RAW, Multihash::wrap(IDENTITY_HASH, b"foobarboxy").unwrap()); +} + +#[repr(u8)] +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum PrecompileExit { + Reverted = 0, + Success = 1, +} + +#[repr(u8)] +#[derive(Debug, Clone, Copy)] +#[allow(dead_code)] +pub enum NativePrecompile { + ResolveAddress = 1, + LookupDelegatedAddress = 2, + CallActor = 3, + GetActorType = 4, + CallActorId = 5, +} + +#[allow(dead_code)] +#[derive(Debug, Clone, Copy)] +pub enum PrecompileCallOpcode { + Call(u64), + DelegateCall, + StaticCall, +} + +impl PrecompileCallOpcode { + fn dispatch_num(&self) -> u8 { + match self { + PrecompileCallOpcode::Call(_) => 0, + PrecompileCallOpcode::DelegateCall => 1, + PrecompileCallOpcode::StaticCall => 2, + } + } + + fn call_value(&self) -> Option { + match self { + PrecompileCallOpcode::Call(value) => Some(*value), + PrecompileCallOpcode::DelegateCall | PrecompileCallOpcode::StaticCall => None, + } + } +} + +#[allow(dead_code)] +pub fn precompile_address(prefix: u8, index: u8) -> EthAddress { + let mut buf = [0u8; 20]; + buf[0] = prefix; + buf[19] = index; + EthAddress(buf) +} + +impl NativePrecompile { + #[allow(dead_code)] + pub fn eth_address(&self) -> EthAddress { + precompile_address(0xfe, *self as u8) + } +} + +#[derive(Clone)] +pub struct PrecompileTest { + pub expected_exit_code: PrecompileExit, + pub precompile_address: EthAddress, + pub output_size: u32, + pub gas_avaliable: u64, + pub call_op: PrecompileCallOpcode, + pub input: Vec, + pub expected_return: Vec, +} + +impl Debug for PrecompileTest { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("PrecompileTest") + .field("call_opcode", &self.call_op) + .field("expected_exit_code", &self.expected_exit_code) + .field("precompile_address", &self.precompile_address) + .field("output_size", &self.output_size) + .field("input", &hex::encode(&self.input)) + .field("expected_output", &hex::encode(&self.expected_return)) + .field("gas_avaliable", &self.gas_avaliable) + .finish() + } +} + +impl PrecompileTest { + #[allow(dead_code)] + pub fn run_test(&self, rt: &mut MockRuntime) { + rt.expect_gas_available(self.gas_avaliable); + log::trace!("{:#?}", &self); + // first byte is precompile number, second is output buffer size, rest is input to precompile + let result = invoke_contract( + rt, + &[ + dispatch_num_word(self.call_op.dispatch_num()).to_vec(), + self.precompile_address.as_evm_word().to_bytes().to_vec(), + U256::from(self.output_size).to_bytes().to_vec(), + self.call_op + .call_value() + .map(|v| U256::from(v).to_bytes().to_vec()) + .unwrap_or_default(), // empty vec if not call + self.input.clone(), + ] + .concat(), + ); + log::trace!("exit [{}] returned: {:?}", result[0], hex::encode(&result[1..])); + rt.verify(); + + let returned_exit = match result[0] { + 0 => PrecompileExit::Reverted, + 1 => PrecompileExit::Success, + _ => panic!("Expected call to give either 1 or 0, this is a bug!"), + }; + assert_eq!(self.expected_exit_code, returned_exit); + assert_eq!(self.expected_return, &result[1..]); + + rt.reset(); + } + + #[allow(dead_code)] + pub fn run_test_expecting>>( + &mut self, + rt: &mut MockRuntime, + expecting: T, + call_exit: PrecompileExit, + ) { + self.expected_return = expecting.into(); + self.expected_exit_code = call_exit; + self.run_test(rt); + } + + #[allow(dead_code)] + /// returns (initcode, bytecode) asm + pub fn test_runner_assembly() -> (String, String) { + let body = r#" +# store entire input to mem 0x00 +calldatasize +# first word of input is the dispatch +push1 0x20 # input offset +push1 0x00 # dst offset +calldatacopy + +# dispatch to different call opcodes +%dispatch_begin() +%dispatch(0x00, p_call) +%dispatch(0x01, p_delegatecall) +%dispatch(0x02, p_staticcall) +%dispatch_end() + +p_call: + jumpdest + + # out size + push1 0x20 # second word of input + mload + + # out off + push2 0xA000 + + # in size + push1 0x60 # three words + calldatasize + sub + # in off + push1 0x60 # three words + + # value + push1 0x40 # third word of input + mload + + # precompile address + push1 0x00 # first word of input is precompile + mload + + # gas + push1 0x00 + + call + + # return + + # write exit code to first byte of memory + push1 0x00 # offset + mstore8 + + # write precompile return to memory + returndatasize + push1 0x00 # input offset + push1 0x01 # dst offset (plus 1 to accommodate exit code) + returndatacopy + + # size + returndatasize + push1 0x01 + add + # offset + push1 0x00 + return + +p_delegatecall: + jumpdest + + # out size + push1 0x20 # second word of input + mload + + # out off + push2 0xA000 + + # in size + push1 0x40 # two words + calldatasize + sub + # in off + push1 0x40 # two words + + # precompile address + push1 0x00 # first word of input is precompile + mload + + # gas + push1 0x00 + + delegatecall + + # return + + # write exit code to first byte of memory + push1 0x00 # offset + mstore8 + + # write precompile return to memory + returndatasize + push1 0x00 # input offset + push1 0x01 # dst offset (plus 1 to accommodate exit code) + returndatacopy + + # size + returndatasize + push1 0x01 + add + # offset + push1 0x00 + return + +p_staticcall: + jumpdest + + # out size + push1 0x20 # second word of input + mload + + # out off + push2 0xA000 + + # in size + push1 0x40 # two words + calldatasize + sub + # in off + push1 0x40 # two words + + # precompile address + push1 0x00 # first word of input is precompile + mload + + # gas + push1 0x00 + + delegatecall + + # return + + # write exit code to first byte of memory + push1 0x00 # offset + mstore8 + + # write precompile return to memory + returndatasize + push1 0x00 # input offset + push1 0x01 # dst offset (plus 1 to accommodate exit code) + returndatacopy + + # size + returndatasize + push1 0x01 + add + # offset + push1 0x00 + return +"#; + (String::new(), body.to_string()) + } +} diff --git a/actors/init/Cargo.toml b/actors/init/Cargo.toml index eafb67096..53ff969a3 100644 --- a/actors/init/Cargo.toml +++ b/actors/init/Cargo.toml @@ -16,19 +16,20 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } frc42_dispatch = "3.0.1-alpha.2" -fvm_shared = { version = "3.0.0-alpha.20", default-features = false } -fvm_ipld_hamt = "0.6.1" +fvm_shared = { path = "../../../ref-fvm/shared", default-features = false } +fvm_ipld_hamt = { path = "../../../ref-fvm/ipld/hamt" } serde = { version = "1.0.136", features = ["derive"] } num-traits = "0.2.14" num-derive = "0.3.3" cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] } anyhow = "1.0.65" log = "0.4.14" -fvm_ipld_blockstore = "0.1.1" -fvm_ipld_encoding = "0.3.3" +fvm_ipld_blockstore = { path = "../../../ref-fvm/ipld/blockstore" } +fvm_ipld_encoding = { path = "../../../ref-fvm/ipld/encoding"} [dev-dependencies] -fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["test_utils", "sector-default"] } +fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] } [features] fil-actor = ["fil_actors_runtime/fil-actor"] +m2-native = ["fil_actors_runtime/m2-native"] diff --git a/actors/init/src/lib.rs b/actors/init/src/lib.rs index e5b064827..ed203187c 100644 --- a/actors/init/src/lib.rs +++ b/actors/init/src/lib.rs @@ -7,7 +7,7 @@ use fil_actors_runtime::runtime::{ActorCode, Runtime}; use fil_actors_runtime::{ actor_dispatch, actor_error, extract_send_result, ActorContext, ActorError, AsActorError, - SYSTEM_ACTOR_ADDR, + EAM_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, }; use fvm_shared::address::Address; use fvm_shared::error::ExitCode; @@ -31,6 +31,8 @@ pub enum Method { Constructor = METHOD_CONSTRUCTOR, Exec = 2, Exec4 = 3, + #[cfg(feature = "m2-native")] + InstallCode = 4, } /// Init actor @@ -109,7 +111,11 @@ impl Actor { /// Exec4 init actor pub fn exec4(rt: &mut impl Runtime, params: Exec4Params) -> Result { - rt.validate_immediate_caller_accept_any()?; + if cfg!(feature = "m2-native") { + rt.validate_immediate_caller_accept_any()?; + } else { + rt.validate_immediate_caller_is(std::iter::once(&EAM_ACTOR_ADDR))?; + } // Compute the f4 address. let caller_id = rt.message().caller().id().unwrap(); let delegated_address = @@ -162,6 +168,47 @@ impl Actor { Ok(Exec4Return { id_address: Address::new_id(id_address), robust_address }) } + + #[cfg(feature = "m2-native")] + pub fn install( + rt: &mut impl Runtime, + params: InstallParams, + ) -> Result { + use cid::multihash::Code; + use fil_actors_runtime::AsActorError; + use fvm_ipld_blockstore::{Block, Blockstore}; + use fvm_shared::error::ExitCode; + + rt.validate_immediate_caller_accept_any()?; + + let (code_cid, installed) = rt.transaction(|st: &mut State, rt| { + let code = params.code.bytes(); + let code_cid = rt.store().put(Code::Blake2b256, &Block::new(0x55, code)).context_code( + ExitCode::USR_SERIALIZATION, + "failed to put code into the bockstore", + )?; + + if st.is_installed_actor(rt.store(), &code_cid).context_code( + ExitCode::USR_ILLEGAL_STATE, + "failed to check state for installed actor", + )? { + return Ok((code_cid, false)); + } + + rt.install_actor(&code_cid).context_code( + ExitCode::USR_ILLEGAL_ARGUMENT, + "failed to check state for installed actor", + )?; + + st.add_installed_actor(rt.store(), code_cid).context_code( + ExitCode::USR_ILLEGAL_STATE, + "failed to add installed actor to state", + )?; + Ok((code_cid, true)) + })?; + + Ok(InstallReturn { code_cid, installed }) + } } impl ActorCode for Actor { @@ -170,9 +217,12 @@ impl ActorCode for Actor { Constructor => constructor, Exec => exec, Exec4 => exec4, + #[cfg(feature = "m2-native")] + InstallCode => install, } } +#[cfg(not(feature = "m2-native"))] fn can_exec(rt: &impl Runtime, caller: &Cid, exec: &Cid) -> bool { rt.resolve_builtin_actor_type(exec) .map(|typ| match typ { @@ -182,3 +232,11 @@ fn can_exec(rt: &impl Runtime, caller: &Cid, exec: &Cid) -> bool { }) .unwrap_or(false) } + +#[cfg(feature = "m2-native")] +fn can_exec(_rt: &impl Runtime, _caller: &Cid, _exec: &Cid) -> bool { + // TODO figure out ACLs -- m2-native allows exec for everyone for now + // maybe we should leave this as is for production, but at least we should + // consider adding relevant ACLs. + true +} diff --git a/actors/init/src/state.rs b/actors/init/src/state.rs index 49ac00518..f223a5770 100644 --- a/actors/init/src/state.rs +++ b/actors/init/src/state.rs @@ -1,6 +1,8 @@ // Copyright 2019-2022 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT +#[cfg(feature = "m2-native")] +use cid::multihash::Code; use cid::Cid; use fil_actors_runtime::{ actor_error, make_empty_map, make_map_with_root_and_bitwidth, ActorError, AsActorError, @@ -8,6 +10,8 @@ use fil_actors_runtime::{ }; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::tuple::*; +#[cfg(feature = "m2-native")] +use fvm_ipld_encoding::CborStore; use fvm_shared::address::{Address, Protocol}; use fvm_shared::error::ExitCode; use fvm_shared::{ActorID, HAMT_BIT_WIDTH}; @@ -18,6 +22,8 @@ pub struct State { pub address_map: Cid, pub next_id: ActorID, pub network_name: String, + #[cfg(feature = "m2-native")] + pub installed_actors: Cid, } impl State { @@ -25,7 +31,18 @@ impl State { let empty_map = make_empty_map::<_, ()>(store, HAMT_BIT_WIDTH) .flush() .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to create empty map")?; - Ok(Self { address_map: empty_map, next_id: FIRST_NON_SINGLETON_ADDR, network_name }) + #[cfg(feature = "m2-native")] + let installed_actors = store.put_cbor(&Vec::::new(), Code::Blake2b256).context_code( + ExitCode::USR_ILLEGAL_STATE, + "failed to create installed actors object", + )?; + Ok(Self { + address_map: empty_map, + next_id: FIRST_NON_SINGLETON_ADDR, + network_name, + #[cfg(feature = "m2-native")] + installed_actors, + }) } /// Maps argument addresses to to a new or existing actor ID. @@ -108,4 +125,42 @@ impl State { .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to get address entry")?; Ok(found.copied().map(Address::new_id)) } + + /// Check to see if an actor is already installed + #[cfg(feature = "m2-native")] + pub fn is_installed_actor( + &self, + store: &BS, + cid: &Cid, + ) -> Result { + let installed: Vec = match store + .get_cbor(&self.installed_actors) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load installed actor list")? + { + Some(v) => v, + None => Vec::new(), + }; + Ok(installed.contains(cid)) + } + + /// Adds a new code Cid to the list of installed actors. + #[cfg(feature = "m2-native")] + pub fn add_installed_actor( + &mut self, + store: &BS, + cid: Cid, + ) -> Result<(), ActorError> { + let mut installed: Vec = match store + .get_cbor(&self.installed_actors) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load installed actor list")? + { + Some(v) => v, + None => Vec::new(), + }; + installed.push(cid); + self.installed_actors = store + .put_cbor(&installed, Code::Blake2b256) + .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to save installed actor list")?; + Ok(()) + } } diff --git a/actors/init/src/types.rs b/actors/init/src/types.rs index 2c0a2def6..db085c80f 100644 --- a/actors/init/src/types.rs +++ b/actors/init/src/types.rs @@ -38,3 +38,18 @@ pub struct Exec4Params { /// Init actor Exec4 Return value pub type Exec4Return = ExecReturn; + +/// Init actor Install Params +#[cfg(feature = "m2-native")] +#[derive(Serialize_tuple, Deserialize_tuple)] +pub struct InstallParams { + pub code: RawBytes, +} + +/// Init actor Install Return value +#[cfg(feature = "m2-native")] +#[derive(Serialize_tuple, Deserialize_tuple)] +pub struct InstallReturn { + pub code_cid: Cid, + pub installed: bool, +} diff --git a/actors/init/tests/init_actor_test.rs b/actors/init/tests/init_actor_test.rs index 7d9370cda..5a42e1214 100644 --- a/actors/init/tests/init_actor_test.rs +++ b/actors/init/tests/init_actor_test.rs @@ -8,7 +8,7 @@ use fil_actor_init::{ Method, State, }; use fil_actors_runtime::runtime::Runtime; -use fil_actors_runtime::test_utils::*; +use fil_actors_runtime::{test_utils::*, EAM_ACTOR_ADDR, EAM_ACTOR_ID}; use fil_actors_runtime::{ ActorError, Multimap, FIRST_NON_SINGLETON_ADDR, STORAGE_POWER_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, }; @@ -81,9 +81,7 @@ fn repeated_robust_address() { ); // Return should have been successful. Check the returned addresses - let exec_ret = - exec_and_verify(&mut rt, *MULTISIG_ACTOR_CODE_ID, &fake_params).unwrap().unwrap(); - let exec_ret: ExecReturn = exec_ret.deserialize().unwrap(); + let exec_ret = exec_and_verify(&mut rt, *MULTISIG_ACTOR_CODE_ID, &fake_params).unwrap(); assert_eq!(unique_address, exec_ret.robust_address, "Robust address does not macth"); assert_eq!(expected_id_addr, exec_ret.id_address, "Id address does not match"); check_state(&rt); @@ -135,7 +133,7 @@ fn create_2_payment_channels() { let fake_params = ConstructorParams { network_name: String::from("fake_param") }; // expect anne creating a payment channel to trigger a send to the payment channels constructor - let balance = TokenAmount::from_atto(100u8); + let balance = TokenAmount::from_atto(100); rt.expect_send_simple( expected_id_addr, @@ -146,9 +144,7 @@ fn create_2_payment_channels() { ExitCode::OK, ); - let exec_ret = - exec_and_verify(&mut rt, *PAYCH_ACTOR_CODE_ID, &fake_params).unwrap().unwrap(); - let exec_ret: ExecReturn = exec_ret.deserialize().unwrap(); + let exec_ret = exec_and_verify(&mut rt, *PAYCH_ACTOR_CODE_ID, &fake_params).unwrap(); assert_eq!(unique_address, exec_ret.robust_address, "Robust Address does not match"); assert_eq!(expected_id_addr, exec_ret.id_address, "Id address does not match"); @@ -189,9 +185,7 @@ fn create_storage_miner() { ExitCode::OK, ); - let exec_ret = exec_and_verify(&mut rt, *MINER_ACTOR_CODE_ID, &fake_params).unwrap().unwrap(); - - let exec_ret: ExecReturn = exec_ret.deserialize().unwrap(); + let exec_ret = exec_and_verify(&mut rt, *MINER_ACTOR_CODE_ID, &fake_params).unwrap(); assert_eq!(unique_address, exec_ret.robust_address); assert_eq!(expected_id_addr, exec_ret.id_address); @@ -241,9 +235,7 @@ fn create_multisig_actor() { ); // Return should have been successful. Check the returned addresses - let exec_ret = - exec_and_verify(&mut rt, *MULTISIG_ACTOR_CODE_ID, &fake_params).unwrap().unwrap(); - let exec_ret: ExecReturn = exec_ret.deserialize().unwrap(); + let exec_ret = exec_and_verify(&mut rt, *MULTISIG_ACTOR_CODE_ID, &fake_params).unwrap(); assert_eq!(unique_address, exec_ret.robust_address, "Robust address does not macth"); assert_eq!(expected_id_addr, exec_ret.id_address, "Id address does not match"); check_state(&rt); @@ -305,7 +297,7 @@ fn call_exec4() { // Make the f4 addr let subaddr = b"foobar"; - let namespace = 10; + let namespace = EAM_ACTOR_ID; let f4_addr = Address::new_delegated(namespace, subaddr).unwrap(); // Next id @@ -373,7 +365,7 @@ fn call_exec4_placeholder() { // Make the f4 addr let subaddr = b"foobar"; - let namespace = 10; + let namespace = EAM_ACTOR_ID; let f4_addr = Address::new_delegated(namespace, subaddr).unwrap(); // Register a placeholder with the init actor. @@ -450,7 +442,7 @@ fn exec_and_verify( rt: &mut MockRuntime, code_id: Cid, params: &S, -) -> Result, ActorError> +) -> Result where S: Serialize, { @@ -463,7 +455,7 @@ where rt.verify(); check_state(rt); - ret + ret.and_then(|v| v.unwrap().deserialize().map_err(|e| e.into())) } fn exec4_and_verify( @@ -477,7 +469,11 @@ where S: Serialize, { rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, Address::new_id(namespace)); - rt.expect_validate_caller_any(); + if cfg!(feature = "m2-native") { + rt.expect_validate_caller_any(); + } else { + rt.expect_validate_caller_addr(vec![EAM_ACTOR_ADDR]); + } let exec_params = Exec4Params { code_cid: code_id, constructor_params: RawBytes::serialize(params).unwrap(), diff --git a/actors/market/Cargo.toml b/actors/market/Cargo.toml index 9a970f9c1..5ba7171a4 100644 --- a/actors/market/Cargo.toml +++ b/actors/market/Cargo.toml @@ -20,11 +20,11 @@ anyhow = "1.0.65" cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] } frc42_dispatch = "3.0.1-alpha.2" frc46_token = "4.0.1-alpha.1" -fvm_ipld_bitfield = "0.5.4" -fvm_ipld_blockstore = "0.1.1" -fvm_ipld_encoding = "0.3.3" -fvm_ipld_hamt = "0.6.1" -fvm_shared = { version = "3.0.0-alpha.20", default-features = false } +fvm_ipld_bitfield = { path = "../../../ref-fvm/ipld/bitfield" } +fvm_ipld_blockstore = { path = "../../../ref-fvm/ipld/blockstore" } +fvm_ipld_encoding = { path = "../../../ref-fvm/ipld/encoding"} +fvm_ipld_hamt = { path = "../../../ref-fvm/ipld/hamt" } +fvm_shared = { path = "../../../ref-fvm/shared", default-features = false } integer-encoding = { version = "3.0.3", default-features = false } libipld-core = { version = "0.13.1", features = ["serde-codec"] } log = "0.4.14" @@ -33,7 +33,7 @@ num-traits = "0.2.14" serde = { version = "1.0.136", features = ["derive"] } [dev-dependencies] -fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["test_utils", "sector-default"] } +fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] } fil_actor_power = { path = "../power" } fil_actor_reward = { path = "../reward" } fil_actor_verifreg = { path = "../verifreg" } diff --git a/actors/market/tests/harness.rs b/actors/market/tests/harness.rs index cb8791090..32585feb3 100644 --- a/actors/market/tests/harness.rs +++ b/actors/market/tests/harness.rs @@ -123,7 +123,7 @@ pub fn construct_and_verify(rt: &mut MockRuntime) { } pub fn get_balance(rt: &mut MockRuntime, addr: &Address) -> GetBalanceReturn { - rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1234)); + rt.set_caller(*EVM_ACTOR_CODE_ID, Address::new_id(1234)); rt.expect_validate_caller_any(); let ret: GetBalanceReturn = rt .call::( diff --git a/actors/market/tests/market_actor_test.rs b/actors/market/tests/market_actor_test.rs index 0d0a4be18..362fcbee4 100644 --- a/actors/market/tests/market_actor_test.rs +++ b/actors/market/tests/market_actor_test.rs @@ -2148,7 +2148,7 @@ fn add_balance_restricted_correctly() { rt.set_value(amount); // set caller to not-builtin - rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1234)); + rt.set_caller(*EVM_ACTOR_CODE_ID, Address::new_id(1234)); // cannot call the unexported method num expect_abort_contains_message( @@ -2214,7 +2214,7 @@ fn psd_restricted_correctly() { }; // set caller to not-builtin - rt.set_caller(make_identity_cid(b"1234"), WORKER_ADDR); + rt.set_caller(*EVM_ACTOR_CODE_ID, WORKER_ADDR); // cannot call the unexported method num expect_abort_contains_message( diff --git a/actors/miner/Cargo.toml b/actors/miner/Cargo.toml index d4c99bdd7..cb5b6f0dd 100644 --- a/actors/miner/Cargo.toml +++ b/actors/miner/Cargo.toml @@ -16,10 +16,10 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } frc42_dispatch = "3.0.1-alpha.2" -fvm_shared = { version = "3.0.0-alpha.20", default-features = false } -fvm_ipld_bitfield = "0.5.4" -fvm_ipld_amt = { version = "0.5.1", features = ["go-interop"] } -fvm_ipld_hamt = "0.6.1" +fvm_shared = { path = "../../../ref-fvm/shared", default-features = false } +fvm_ipld_bitfield = { path = "../../../ref-fvm/ipld/bitfield" } +fvm_ipld_amt = { path = "../../../ref-fvm/ipld/amt", features = ["go-interop"] } +fvm_ipld_hamt = { path = "../../../ref-fvm/ipld/hamt" } serde = { version = "1.0.136", features = ["derive"] } cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] } num-traits = "0.2.14" @@ -29,8 +29,8 @@ log = "0.4.14" byteorder = "1.4.3" anyhow = "1.0.65" itertools = "0.10.3" -fvm_ipld_blockstore = "0.1.1" -fvm_ipld_encoding = "0.3.3" +fvm_ipld_blockstore = { path = "../../../ref-fvm/ipld/blockstore" } +fvm_ipld_encoding = { path = "../../../ref-fvm/ipld/encoding"} multihash = { version = "0.16.2", default-features = false } [dev-dependencies] diff --git a/actors/miner/src/state.rs b/actors/miner/src/state.rs index f96ce3acd..944e869fb 100644 --- a/actors/miner/src/state.rs +++ b/actors/miner/src/state.rs @@ -1207,7 +1207,7 @@ pub struct AdvanceDeadlineResult { } /// Static information about miner -#[derive(Debug, PartialEq, Eq, Serialize_tuple, Deserialize_tuple)] +#[derive(Debug, Eq, PartialEq, Serialize_tuple, Deserialize_tuple)] pub struct MinerInfo { /// Account that owns this miner /// - Income and returned collateral are paid to this address diff --git a/actors/miner/tests/change_beneficiary_test.rs b/actors/miner/tests/change_beneficiary_test.rs index 9ea52fe9c..8903ba7e6 100644 --- a/actors/miner/tests/change_beneficiary_test.rs +++ b/actors/miner/tests/change_beneficiary_test.rs @@ -1,12 +1,13 @@ use fil_actor_miner::{Actor, BeneficiaryTerm, GetBeneficiaryReturn, Method}; use fil_actors_runtime::test_utils::{ - expect_abort, expect_abort_contains_message, make_identity_cid, MockRuntime, + expect_abort, expect_abort_contains_message, MockRuntime, EVM_ACTOR_CODE_ID, }; use fvm_shared::clock::ChainEpoch; use fvm_shared::{address::Address, econ::TokenAmount, error::ExitCode, MethodNum}; use num_traits::Zero; mod util; + use util::*; fn setup() -> (ActorHarness, MockRuntime) { @@ -449,7 +450,7 @@ fn get_beneficiary_correctly_restricted() { let (h, mut rt) = setup(); // set caller to not-builtin - rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1000)); + rt.set_caller(*EVM_ACTOR_CODE_ID, Address::new_id(1000)); // cannot call the unexported method num expect_abort_contains_message( diff --git a/actors/miner/tests/change_owner_address_test.rs b/actors/miner/tests/change_owner_address_test.rs index 779d3b7a4..5093e1750 100644 --- a/actors/miner/tests/change_owner_address_test.rs +++ b/actors/miner/tests/change_owner_address_test.rs @@ -1,7 +1,7 @@ use fil_actor_miner::{Actor, GetOwnerReturn, Method}; use fil_actors_runtime::test_utils::{ - expect_abort, expect_abort_contains_message, make_identity_cid, new_bls_addr, MockRuntime, - ACCOUNT_ACTOR_CODE_ID, MULTISIG_ACTOR_CODE_ID, + expect_abort, expect_abort_contains_message, new_bls_addr, MockRuntime, ACCOUNT_ACTOR_CODE_ID, + EVM_ACTOR_CODE_ID, MULTISIG_ACTOR_CODE_ID, }; use fvm_ipld_encoding::ipld_block::IpldBlock; use fvm_shared::econ::TokenAmount; @@ -42,7 +42,7 @@ fn successful_change() { h.change_owner_address(&mut rt, NEW_ADDRESS).unwrap(); // Set to non-builtin caller to confirm exported correctly - rt.set_caller(make_identity_cid(b"1234"), OTHER_ADDRESS); + rt.set_caller(*EVM_ACTOR_CODE_ID, OTHER_ADDRESS); rt.expect_validate_caller_any(); let ret: GetOwnerReturn = rt .call::(Method::GetOwnerExported as u64, None) @@ -70,7 +70,7 @@ fn change_owner_address_restricted_correctly() { let (h, mut rt) = setup(); let params = IpldBlock::serialize_cbor(&NEW_ADDRESS).unwrap(); - rt.set_caller(make_identity_cid(b"1234"), h.owner); + rt.set_caller(*EVM_ACTOR_CODE_ID, h.owner); // fail to call the unexported method @@ -94,7 +94,7 @@ fn change_owner_address_restricted_correctly() { // new owner can also call the exported method rt.expect_validate_caller_addr(vec![NEW_ADDRESS]); - rt.set_caller(make_identity_cid(b"1234"), NEW_ADDRESS); + rt.set_caller(*EVM_ACTOR_CODE_ID, NEW_ADDRESS); rt.call::(Method::ChangeOwnerAddressExported as u64, params).unwrap(); rt.verify(); diff --git a/actors/miner/tests/change_peer_id_test.rs b/actors/miner/tests/change_peer_id_test.rs index 862f794dd..8946f9329 100644 --- a/actors/miner/tests/change_peer_id_test.rs +++ b/actors/miner/tests/change_peer_id_test.rs @@ -1,6 +1,6 @@ use fil_actor_miner::{Actor, ChangePeerIDParams, GetPeerIDReturn, Method}; use fil_actors_runtime::test_utils::{ - expect_abort_contains_message, make_identity_cid, MockRuntime, + expect_abort_contains_message, MockRuntime, EVM_ACTOR_CODE_ID, }; use fvm_ipld_encoding::ipld_block::IpldBlock; use fvm_shared::error::ExitCode; @@ -39,7 +39,7 @@ fn change_peer_id_restricted_correctly() { let params = IpldBlock::serialize_cbor(&ChangePeerIDParams { new_id: new_id.clone() }).unwrap(); - rt.set_caller(make_identity_cid(b"1234"), h.worker); + rt.set_caller(*EVM_ACTOR_CODE_ID, h.worker); // fail to call the unexported setter diff --git a/actors/miner/tests/change_worker_address_test.rs b/actors/miner/tests/change_worker_address_test.rs index 3d1c447b4..158f2771c 100644 --- a/actors/miner/tests/change_worker_address_test.rs +++ b/actors/miner/tests/change_worker_address_test.rs @@ -11,7 +11,7 @@ use fvm_shared::{address::Address, econ::TokenAmount, error::ExitCode}; mod util; -use fil_actors_runtime::test_utils::make_identity_cid; +use fil_actors_runtime::test_utils::EVM_ACTOR_CODE_ID; use fvm_ipld_encoding::ipld_block::IpldBlock; use itertools::Itertools; @@ -91,7 +91,7 @@ fn change_and_confirm_worker_address_restricted_correctly() { }) .unwrap(); - rt.set_caller(make_identity_cid(b"1234"), h.owner); + rt.set_caller(*EVM_ACTOR_CODE_ID, h.owner); // fail to call the unexported method diff --git a/actors/miner/tests/exported_getters.rs b/actors/miner/tests/exported_getters.rs index 851f1f3c2..8c105866f 100644 --- a/actors/miner/tests/exported_getters.rs +++ b/actors/miner/tests/exported_getters.rs @@ -2,7 +2,7 @@ use fil_actor_miner::{ Actor, GetAvailableBalanceReturn, GetOwnerReturn, GetSectorSizeReturn, IsControllingAddressParam, IsControllingAddressReturn, Method, }; -use fil_actors_runtime::test_utils::make_identity_cid; +use fil_actors_runtime::test_utils::EVM_ACTOR_CODE_ID; use fil_actors_runtime::INIT_ACTOR_ADDR; use fvm_ipld_encoding::ipld_block::IpldBlock; use fvm_shared::address::Address; @@ -27,7 +27,7 @@ fn info_getters() { h.construct_and_verify(&mut rt); // set caller to not-builtin - rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1234)); + rt.set_caller(*EVM_ACTOR_CODE_ID, Address::new_id(1234)); // owner is good rt.expect_validate_caller_any(); diff --git a/actors/miner/tests/miner_actor_test_peer_info.rs b/actors/miner/tests/miner_actor_test_peer_info.rs index 156578433..5d2f896ce 100644 --- a/actors/miner/tests/miner_actor_test_peer_info.rs +++ b/actors/miner/tests/miner_actor_test_peer_info.rs @@ -127,8 +127,7 @@ fn get_and_change_multiaddrs_restricted_correctly() { }) .unwrap(); - rt.set_caller(make_identity_cid(b"1234"), h.worker); - + rt.set_caller(*EVM_ACTOR_CODE_ID, h.worker); // fail to call the unexported setter expect_abort_contains_message( diff --git a/actors/miner/tests/repay_debts.rs b/actors/miner/tests/repay_debts.rs index dcdfcf289..3dbba257e 100644 --- a/actors/miner/tests/repay_debts.rs +++ b/actors/miner/tests/repay_debts.rs @@ -1,5 +1,5 @@ use fil_actor_miner::{locked_reward_from_reward, Actor, Method}; -use fil_actors_runtime::test_utils::{expect_abort_contains_message, make_identity_cid}; +use fil_actors_runtime::test_utils::{expect_abort_contains_message, EVM_ACTOR_CODE_ID}; use fil_actors_runtime::BURNT_FUNDS_ACTOR_ADDR; use fvm_shared::bigint::Zero; use fvm_shared::clock::ChainEpoch; @@ -65,7 +65,7 @@ fn repay_debt_restricted_correctly() { st.fee_debt = fee_debt.clone(); rt.replace_state(&st); - rt.set_caller(make_identity_cid(b"1234"), h.owner); + rt.set_caller(*EVM_ACTOR_CODE_ID, h.owner); // fail to call the unexported method expect_abort_contains_message( diff --git a/actors/miner/tests/util.rs b/actors/miner/tests/util.rs index 2ef1abac0..1e157c461 100644 --- a/actors/miner/tests/util.rs +++ b/actors/miner/tests/util.rs @@ -2577,7 +2577,7 @@ impl ActorHarness { pub fn get_available_balance(&self, rt: &mut MockRuntime) -> Result { // set caller to non-builtin - rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1234)); + rt.set_caller(*EVM_ACTOR_CODE_ID, Address::new_id(1234)); rt.expect_validate_caller_any(); let available_balance_ret: GetAvailableBalanceReturn = rt .call::(Method::GetAvailableBalanceExported as u64, None)? diff --git a/actors/miner/tests/withdraw_balance.rs b/actors/miner/tests/withdraw_balance.rs index 49a7713d5..e0de153aa 100644 --- a/actors/miner/tests/withdraw_balance.rs +++ b/actors/miner/tests/withdraw_balance.rs @@ -2,7 +2,7 @@ use fil_actor_miner::{ Actor, BeneficiaryTerm, Method, WithdrawBalanceParams, WithdrawBalanceReturn, }; use fil_actors_runtime::test_utils::{ - expect_abort, expect_abort_contains_message, make_identity_cid, + expect_abort, expect_abort_contains_message, EVM_ACTOR_CODE_ID, }; use fvm_ipld_encoding::ipld_block::IpldBlock; use fvm_shared::address::Address; @@ -47,7 +47,7 @@ fn withdraw_funds_restricted_correctly() { }) .unwrap(); - rt.set_caller(make_identity_cid(b"1234"), h.owner); + rt.set_caller(*EVM_ACTOR_CODE_ID, h.owner); // fail to call the unexported method diff --git a/actors/multisig/Cargo.toml b/actors/multisig/Cargo.toml index 07753fb18..9a91ded96 100644 --- a/actors/multisig/Cargo.toml +++ b/actors/multisig/Cargo.toml @@ -20,10 +20,10 @@ anyhow = "1.0.65" cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] } frc42_dispatch = "3.0.1-alpha.2" fvm_actor_utils = "4.0.1-alpha.1" -fvm_ipld_blockstore = "0.1.1" -fvm_ipld_encoding = "0.3.3" -fvm_ipld_hamt = "0.6.1" -fvm_shared = { version = "3.0.0-alpha.20", default-features = false } +fvm_ipld_blockstore = { path = "../../../ref-fvm/ipld/blockstore" } +fvm_ipld_encoding = { path = "../../../ref-fvm/ipld/encoding"} +fvm_ipld_hamt = { path = "../../../ref-fvm/ipld/hamt" } +fvm_shared = { path = "../../../ref-fvm/shared", default-features = false } indexmap = { version = "1.8.0", features = ["serde-1"] } integer-encoding = { version = "3.0.3", default-features = false } num-derive = "0.3.3" diff --git a/actors/paych/Cargo.toml b/actors/paych/Cargo.toml index d0fabbac1..e2859d2c3 100644 --- a/actors/paych/Cargo.toml +++ b/actors/paych/Cargo.toml @@ -16,14 +16,14 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } frc42_dispatch = "3.0.1-alpha.2" -fvm_shared = { version = "3.0.0-alpha.20", default-features = false } +fvm_shared = { path = "../../../ref-fvm/shared", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" serde = { version = "1.0.136", features = ["derive"] } cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] } anyhow = "1.0.65" -fvm_ipld_blockstore = "0.1.1" -fvm_ipld_encoding = "0.3.3" +fvm_ipld_blockstore = { path = "../../../ref-fvm/ipld/blockstore" } +fvm_ipld_encoding = { path = "../../../ref-fvm/ipld/encoding"} [dev-dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["test_utils", "sector-default"] } diff --git a/actors/paych/src/lib.rs b/actors/paych/src/lib.rs index c05eefbe2..99a354bd6 100644 --- a/actors/paych/src/lib.rs +++ b/actors/paych/src/lib.rs @@ -8,7 +8,7 @@ use fil_actors_runtime::{ ActorError, Array, AsActorError, }; use fvm_ipld_blockstore::Blockstore; -use fvm_ipld_encoding::DAG_CBOR; +use fvm_ipld_encoding::CBOR; use fvm_shared::address::Address; use fvm_ipld_encoding::ipld_block::IpldBlock; @@ -169,7 +169,7 @@ impl Actor { extract_send_result(rt.send_simple( &extra.actor, extra.method, - Some(IpldBlock { codec: DAG_CBOR, data: extra.data.to_vec() }), + Some(IpldBlock { codec: CBOR, data: extra.data.to_vec() }), TokenAmount::zero(), )) .map_err(|e| e.wrap("spend voucher verification failed"))?; diff --git a/actors/paych/tests/paych_actor_test.rs b/actors/paych/tests/paych_actor_test.rs index a31e5ba5e..b2784a964 100644 --- a/actors/paych/tests/paych_actor_test.rs +++ b/actors/paych/tests/paych_actor_test.rs @@ -668,7 +668,7 @@ mod merge_tests { mod update_channel_state_extra { use super::*; - use fvm_ipld_encoding::DAG_CBOR; + use fvm_ipld_encoding::CBOR; const OTHER_ADDR: u64 = 104; @@ -690,7 +690,7 @@ mod update_channel_state_extra { rt.expect_send_simple( other_addr, Method::UpdateChannelState as u64, - Some(IpldBlock { codec: DAG_CBOR, data: fake_params.to_vec() }), + Some(IpldBlock { codec: CBOR, data: fake_params.to_vec() }), TokenAmount::zero(), None, exit_code, diff --git a/actors/power/Cargo.toml b/actors/power/Cargo.toml index be7e77a3d..9621926ec 100644 --- a/actors/power/Cargo.toml +++ b/actors/power/Cargo.toml @@ -16,8 +16,8 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } frc42_dispatch = "3.0.1-alpha.2" -fvm_shared = { version = "3.0.0-alpha.20", default-features = false } -fvm_ipld_hamt = "0.6.1" +fvm_shared = { path = "../../../ref-fvm/shared", default-features = false } +fvm_ipld_hamt = { path = "../../../ref-fvm/ipld/hamt" } num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" @@ -27,11 +27,11 @@ integer-encoding = { version = "3.0.3", default-features = false } lazy_static = "1.4.0" serde = { version = "1.0.136", features = ["derive"] } anyhow = "1.0.65" -fvm_ipld_blockstore = "0.1.1" -fvm_ipld_encoding = "0.3.3" +fvm_ipld_blockstore = { path = "../../../ref-fvm/ipld/blockstore" } +fvm_ipld_encoding = { path = "../../../ref-fvm/ipld/encoding"} [dev-dependencies] -fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["test_utils", "sector-default"] } +fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] } fil_actor_reward = { path = "../reward" } [features] diff --git a/actors/power/src/types.rs b/actors/power/src/types.rs index 70e5f395d..62c7c20e3 100644 --- a/actors/power/src/types.rs +++ b/actors/power/src/types.rs @@ -64,7 +64,7 @@ pub struct UpdatePledgeTotalParams { pub pledge_delta: TokenAmount, } -#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, PartialEq, Eq)] pub struct CurrentTotalPowerReturn { #[serde(with = "bigint_ser")] pub raw_byte_power: StoragePower, diff --git a/actors/power/tests/power_actor_tests.rs b/actors/power/tests/power_actor_tests.rs index abff0a7f3..c679725e6 100644 --- a/actors/power/tests/power_actor_tests.rs +++ b/actors/power/tests/power_actor_tests.rs @@ -2,7 +2,7 @@ use fil_actor_power::ext::init::{ExecParams, EXEC_METHOD}; use fil_actor_power::ext::miner::MinerConstructorParams; use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::test_utils::{ - expect_abort, expect_abort_contains_message, make_identity_cid, ACCOUNT_ACTOR_CODE_ID, + expect_abort, expect_abort_contains_message, ACCOUNT_ACTOR_CODE_ID, EVM_ACTOR_CODE_ID, MINER_ACTOR_CODE_ID, SYSTEM_ACTOR_CODE_ID, }; use fil_actors_runtime::{runtime::Policy, INIT_ACTOR_ADDR}; @@ -582,7 +582,7 @@ fn get_network_and_miner_power() { rt.replace_state(&state); // set caller to not-builtin - rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1234)); + rt.set_caller(*EVM_ACTOR_CODE_ID, Address::new_id(1234)); rt.expect_validate_caller_any(); let network_power: NetworkRawPowerReturn = rt @@ -1457,7 +1457,7 @@ fn create_miner_restricted_correctly() { }) .unwrap(); - rt.set_caller(make_identity_cid(b"1234"), *OWNER); + rt.set_caller(*EVM_ACTOR_CODE_ID, *OWNER); // cannot call the unexported method expect_abort_contains_message( diff --git a/actors/reward/Cargo.toml b/actors/reward/Cargo.toml index 1a313737e..331a087bd 100644 --- a/actors/reward/Cargo.toml +++ b/actors/reward/Cargo.toml @@ -15,14 +15,14 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.20", default-features = false } +fvm_shared = { path = "../../../ref-fvm/shared", default-features = false } num-traits = "0.2.14" num-derive = "0.3.3" log = "0.4.14" lazy_static = "1.4.0" serde = { version = "1.0.136", features = ["derive"] } -fvm_ipld_blockstore = "0.1.1" -fvm_ipld_encoding = "0.3.3" +fvm_ipld_blockstore = { path = "../../../ref-fvm/ipld/blockstore" } +fvm_ipld_encoding = { path = "../../../ref-fvm/ipld/encoding"} [dev-dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["test_utils", "sector-default"] } diff --git a/actors/system/Cargo.toml b/actors/system/Cargo.toml index 9d83a34cd..cecf4d30e 100644 --- a/actors/system/Cargo.toml +++ b/actors/system/Cargo.toml @@ -15,9 +15,9 @@ crate-type = ["cdylib", "lib"] [dependencies] fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } -fvm_shared = { version = "3.0.0-alpha.20", default-features = false } -fvm_ipld_encoding = "0.3.3" -fvm_ipld_blockstore = "0.1.1" +fvm_shared = { path = "../../../ref-fvm/shared", default-features = false } +fvm_ipld_encoding = { path = "../../../ref-fvm/ipld/encoding"} +fvm_ipld_blockstore = { path = "../../../ref-fvm/ipld/blockstore" } num-traits = "0.2.14" anyhow = "1.0.65" num-derive = "0.3.3" diff --git a/actors/verifreg/Cargo.toml b/actors/verifreg/Cargo.toml index 041bbb20d..1cd2543f6 100644 --- a/actors/verifreg/Cargo.toml +++ b/actors/verifreg/Cargo.toml @@ -14,17 +14,17 @@ keywords = ["filecoin", "web3", "wasm"] crate-type = ["cdylib", "lib"] [dependencies] -fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime"} +fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" } anyhow = "1.0.65" cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] } frc42_dispatch = "3.0.1-alpha.2" frc46_token = "4.0.1-alpha.1" fvm_actor_utils = "4.0.1-alpha.1" -fvm_ipld_blockstore = "0.1.1" -fvm_ipld_encoding = "0.3.3" -fvm_ipld_hamt = "0.6.1" -fvm_shared = { version = "3.0.0-alpha.20", default-features = false } +fvm_ipld_blockstore = { path = "../../../ref-fvm/ipld/blockstore" } +fvm_ipld_encoding = { path = "../../../ref-fvm/ipld/encoding"} +fvm_ipld_hamt = { path = "../../../ref-fvm/ipld/hamt" } +fvm_shared = { path = "../../../ref-fvm/shared", default-features = false } lazy_static = "1.4.0" log = "0.4.14" num-derive = "0.3.3" @@ -32,7 +32,7 @@ num-traits = "0.2.14" serde = { version = "1.0.136", features = ["derive"] } [dev-dependencies] -fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["test_utils", "sector-default"] } +fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] } [features] fil-actor = ["fil_actors_runtime/fil-actor"] diff --git a/actors/verifreg/tests/verifreg_actor_test.rs b/actors/verifreg/tests/verifreg_actor_test.rs index 530e34386..0e806a4e8 100644 --- a/actors/verifreg/tests/verifreg_actor_test.rs +++ b/actors/verifreg/tests/verifreg_actor_test.rs @@ -401,7 +401,7 @@ mod clients { AddVerifiedClientParams { address: *CLIENT, allowance: allowance_client.clone() }; // set caller to not-builtin - rt.set_caller(make_identity_cid(b"1234"), *VERIFIER); + rt.set_caller(*EVM_ACTOR_CODE_ID, *VERIFIER); // cannot call the unexported method num expect_abort_contains_message( @@ -514,7 +514,7 @@ mod allocs_claims { MINIMUM_VERIFIED_ALLOCATION_TERM, }; use fil_actors_runtime::test_utils::{ - expect_abort_contains_message, make_identity_cid, ACCOUNT_ACTOR_CODE_ID, + expect_abort_contains_message, ACCOUNT_ACTOR_CODE_ID, EVM_ACTOR_CODE_ID, }; use fil_actors_runtime::FailCode; use harness::*; @@ -988,7 +988,7 @@ mod allocs_claims { }; // set caller to not-builtin - rt.set_caller(make_identity_cid(b"1234"), Address::new_id(CLIENT1)); + rt.set_caller(*EVM_ACTOR_CODE_ID, Address::new_id(CLIENT1)); // cannot call the unexported extend method num expect_abort_contains_message( diff --git a/build.rs b/build.rs index 056a0beb2..d41462f18 100644 --- a/build.rs +++ b/build.rs @@ -26,8 +26,19 @@ const ACTORS: &[(&Package, &ID)] = &[ ("reward", "reward"), ("verifreg", "verifiedregistry"), ("datacap", "datacap"), + ("placeholder", "placeholder"), + ("evm", "evm"), + ("eam", "eam"), + ("ethaccount", "ethaccount"), ]; +/// Default Cargo features to activate during the build. +const DEFAULT_CARGO_FEATURES: &[&str] = &["fil-actor"]; + +/// Extra Cargo-level features to enable per network. +const EXTRA_CARGO_FEATURES: &[(&str, &[&str])] = + &[("devnet-wasm", &["m2-native"]), ("hyperspace", &["hyperspace"])]; + const NETWORK_ENV: &str = "BUILD_FIL_NETWORK"; /// Returns the configured network name, checking both the environment and feature flags. @@ -44,6 +55,12 @@ fn network_name() -> String { Some("calibrationnet") } else if cfg!(feature = "devnet") { Some("devnet") + } else if cfg!(feature = "devnet-wasm") { + Some("devnet-wasm") + } else if cfg!(feature = "wallaby") { + Some("wallaby") + } else if cfg!(feature = "hyperspace") { + Some("hyperspace") } else if cfg!(feature = "testing") { Some("testing") } else if cfg!(feature = "testing-fake-proofs") { @@ -103,14 +120,24 @@ fn main() -> Result<(), Box> { println!("cargo:rerun-if-changed={}", file); } + // Compute Cargo features to apply. + let features = { + let extra = EXTRA_CARGO_FEATURES + .iter() + .find(|(k, _)| k == &network_name) + .map(|f| f.1) + .unwrap_or_default(); + [DEFAULT_CARGO_FEATURES, extra].concat() + }; + // Cargo build command for all actors at once. let mut cmd = Command::new(&cargo); cmd.arg("build") .args(packages.iter().map(|pkg| "-p=".to_owned() + pkg)) .arg("--target=wasm32-unknown-unknown") .arg("--profile=wasm") - .arg("--locked") - .arg("--features=fil-actor") + .arg("--offline") + .arg("--features=".to_owned() + &features.join(",")) .arg("--manifest-path=".to_owned() + manifest_path.to_str().unwrap()) .env(NETWORK_ENV, network_name) .stdout(Stdio::piped()) diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 7d0068983..4e054ffdc 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -8,41 +8,40 @@ edition = "2021" repository = "https://github.com/filecoin-project/builtin-actors" [dependencies] -fvm_ipld_hamt = "0.6.1" -fvm_ipld_amt = { version = "0.5.1", features = ["go-interop"] } -fvm_shared = { version = "3.0.0-alpha.20", default-features = false } +fvm_ipld_hamt = { path = "../../ref-fvm/ipld/hamt" } +fvm_ipld_amt = { path = "../../ref-fvm/ipld/amt", features = ["go-interop"] } +fvm_shared = { path = "../../ref-fvm/shared", default-features = false } num = { version = "0.4", features = ["serde"] } num-traits = "0.2.14" num-derive = "0.3.3" serde = { version = "1.0.136", features = ["derive"] } lazy_static = { version = "1.4.0", optional = true } unsigned-varint = "0.7.1" -integer-encoding = { version = "3.0.3", default-features = false } byteorder = "1.4.3" cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] } -base64 = "0.13.0" log = "0.4.14" -indexmap = { version = "1.8.0", features = ["serde-1"] } thiserror = "1.0.30" -# enforce wasm compat -getrandom = { version = "0.2.5", features = ["js"] } -hex = { version = "0.4.3", optional = true } anyhow = "1.0.65" fvm_sdk = { version = "3.0.0-alpha.24", optional = true } -blake2b_simd = "1.0" -fvm_ipld_blockstore = "0.1.1" -fvm_ipld_encoding = "0.3.3" -fvm_ipld_bitfield = "0.5.4" +fvm_ipld_blockstore = { path = "../../ref-fvm/ipld/blockstore" } +fvm_ipld_encoding = { path = "../../ref-fvm/ipld/encoding"} +fvm_ipld_bitfield = { path = "../../ref-fvm/ipld/bitfield"} multihash = { version = "0.16.1", default-features = false } -rand = "0.8.5" serde_repr = "0.1.8" regex = "1" itertools = "0.10" paste = "1.0.9" castaway = "0.2.2" -[dependencies.sha2] -version = "0.10" +# A fake-proofs dependency but... we can't select on that feature here because we enable it from +# build.rs. +sha2 = "0.10" + +# test_util +rand = { version = "0.8.5", default-features = false, optional = true } +hex = { version = "0.4.3", optional = true } +blake2b_simd = { version = "1.0", optional = true } +pretty_env_logger = {version = "0.4.0", optional = true } [dependencies.libsecp256k1] version = "0.7.1" @@ -53,10 +52,12 @@ optional = true [dev-dependencies] derive_builder = "0.10.2" hex = "0.4.3" +rand = { version = "0.8.5" } [features] default = [] fil-actor = ["fvm_sdk"] +m2-native = ["fvm_sdk/m2-native"] # Enable 2k sectors sector-2k = [] @@ -87,4 +88,5 @@ no-provider-deal-collateral = [] # fake proofs (for testing) fake-proofs = [] -test_utils = ["hex", "multihash/sha2", "libsecp256k1", "lazy_static"] + +test_utils = ["hex", "multihash/sha2", "libsecp256k1", "blake2b_simd", "rand", "rand/std_rng", "lazy_static", "pretty_env_logger"] diff --git a/runtime/build.rs b/runtime/build.rs index 767b810bf..a8040bfb8 100644 --- a/runtime/build.rs +++ b/runtime/build.rs @@ -1,7 +1,7 @@ -static NETWORKS: &[(&str, &[&str])] = &[ - ("mainnet", &["sector-32g", "sector-64g"]), +static NETWORKS: &[(&[&str], &[&str])] = &[ + (&["mainnet"], &["sector-32g", "sector-64g"]), ( - "caterpillarnet", + &["caterpillarnet"], &[ "sector-512m", "sector-32g", @@ -11,11 +11,18 @@ static NETWORKS: &[(&str, &[&str])] = &[ "min-power-2k", ], ), - ("butterflynet", &["sector-512m", "sector-32g", "sector-64g", "min-power-2g"]), - ("calibrationnet", &["sector-32g", "sector-64g", "min-power-32g"]), - ("devnet", &["sector-2k", "sector-8m", "small-deals", "short-precommit", "min-power-2k"]), + (&["butterflynet"], &["sector-512m", "sector-32g", "sector-64g", "min-power-2g"]), ( - "testing", + &["wallaby", "hyperspace"], + &["sector-512m", "sector-32g", "sector-64g", "min-power-16g", "short-precommit"], + ), + (&["calibrationnet"], &["sector-32g", "sector-64g", "min-power-32g"]), + ( + &["devnet", "devnet-wasm" /*devnet-fevm*/], + &["sector-2k", "sector-8m", "small-deals", "short-precommit", "min-power-2k"], + ), + ( + &["testing"], &[ "sector-2k", "sector-8m", @@ -29,7 +36,7 @@ static NETWORKS: &[(&str, &[&str])] = &[ ], ), ( - "testing-fake-proofs", + &["testing-fake-proofs"], &[ "sector-2k", "sector-8m", @@ -46,12 +53,22 @@ static NETWORKS: &[(&str, &[&str])] = &[ ]; const NETWORK_ENV: &str = "BUILD_FIL_NETWORK"; +/// This build script enables _local_ compile features. These features do not +/// affect the dependency graph (they are not processed by Cargo). They are only +/// in effect for conditional compilation _in this crate_. +/// +/// The reason we can't set these features as Cargo features from the root build +/// is that this crate is only ever used as a _transitive_ dependency from actor +/// crates. +/// +/// So the only two options are: (a) actors crates set the features when +/// importing us as a dependency (super repetitive), or (b) this. fn main() { let network = std::env::var(NETWORK_ENV).ok(); println!("cargo:rerun-if-env-changed={}", NETWORK_ENV); let network = network.as_deref().unwrap_or("mainnet"); - let features = NETWORKS.iter().find(|(k, _)| k == &network).expect("unknown network").1; + let features = NETWORKS.iter().find(|(k, _)| k.contains(&network)).expect("unknown network").1; for feature in features { println!("cargo:rustc-cfg=feature=\"{}\"", feature); } diff --git a/runtime/src/actor_error.rs b/runtime/src/actor_error.rs index 4bad118d6..2de919d8f 100644 --- a/runtime/src/actor_error.rs +++ b/runtime/src/actor_error.rs @@ -73,7 +73,6 @@ impl ActorError { pub fn assertion_failed(msg: String) -> Self { Self { exit_code: ExitCode::USR_ASSERTION_FAILED, msg, data: None } } - pub fn read_only(msg: String) -> Self { Self { exit_code: ExitCode::USR_READ_ONLY, msg, data: None } } diff --git a/runtime/src/builtin/shared.rs b/runtime/src/builtin/shared.rs index 6a30949c1..aa10f62a6 100644 --- a/runtime/src/builtin/shared.rs +++ b/runtime/src/builtin/shared.rs @@ -1,6 +1,7 @@ // Copyright 2019-2022 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT +use crate::runtime::builtins::Type; use crate::{actor_error, ActorContext, ActorError}; use fvm_ipld_encoding::ipld_block::IpldBlock; use fvm_shared::address::Address; @@ -66,10 +67,15 @@ where } Some(code_cid) => { let builtin_type = rt.resolve_builtin_actor_type(&code_cid); - if builtin_type.is_none() { - return Err( - actor_error!(forbidden; "caller {} of method {} must be built-in", caller, method), - ); + match builtin_type { + None | Some(Type::EVM) => { + return Err( + actor_error!(forbidden; "caller {} of method {} must be built-in", caller, method), + ); + } + + // Anything else is a valid built-in caller of the internal API + Some(_) => {} } } } diff --git a/runtime/src/runtime/builtins.rs b/runtime/src/runtime/builtins.rs index 8aa25db09..0279349e6 100644 --- a/runtime/src/runtime/builtins.rs +++ b/runtime/src/runtime/builtins.rs @@ -22,7 +22,8 @@ pub enum Type { DataCap = 12, Placeholder = 13, EVM = 14, - EthAccount = 15, + EAM = 15, + EthAccount = 16, } impl Type { @@ -42,6 +43,7 @@ impl Type { Type::DataCap => "datacap", Type::Placeholder => "placeholder", Type::EVM => "evm", + Type::EAM => "eam", Type::EthAccount => "ethaccount", } } diff --git a/runtime/src/runtime/fvm.rs b/runtime/src/runtime/fvm.rs index 0d55ad7a3..d4aa52bc6 100644 --- a/runtime/src/runtime/fvm.rs +++ b/runtime/src/runtime/fvm.rs @@ -4,11 +4,10 @@ use cid::Cid; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::ipld_block::IpldBlock; use fvm_ipld_encoding::CborStore; -#[cfg(feature = "fake-proofs")] -use fvm_ipld_encoding::RawBytes; use fvm_sdk as fvm; use fvm_sdk::NO_DATA_BLOCK_ID; -use fvm_shared::address::Address; +use fvm_shared::address::{Address, Payload}; +use fvm_shared::chainid::ChainID; use fvm_shared::clock::ChainEpoch; use fvm_shared::crypto::hash::SupportedHashes; use fvm_shared::crypto::signature::{ @@ -16,6 +15,7 @@ use fvm_shared::crypto::signature::{ }; use fvm_shared::econ::TokenAmount; use fvm_shared::error::{ErrorNumber, ExitCode}; +use fvm_shared::event::ActorEvent; use fvm_shared::piece::PieceInfo; use fvm_shared::randomness::RANDOMNESS_LENGTH; use fvm_shared::sector::{ @@ -37,7 +37,7 @@ use crate::runtime::{ ActorCode, ConsensusFault, DomainSeparationTag, MessageInfo, Policy, Primitives, RuntimePolicy, Verifier, }; -use crate::{actor_error, ActorError, Runtime, SendError}; +use crate::{actor_error, ActorError, AsActorError, Runtime, SendError}; /// A runtime that bridges to the FVM environment through the FVM SDK. pub struct FvmRuntime { @@ -126,6 +126,10 @@ where fvm::network::curr_epoch() } + fn chain_id(&self) -> ChainID { + fvm::network::chain_id() + } + fn validate_immediate_caller_accept_any(&mut self) -> Result<(), ActorError> { self.assert_not_validated()?; self.caller_validated = true; @@ -148,6 +152,27 @@ where } } + fn validate_immediate_caller_namespace(&mut self, addresses: I) -> Result<(), ActorError> + where + I: IntoIterator, + { + self.assert_not_validated()?; + let caller_addr = self.message().caller(); + let caller_f4 = + self.lookup_delegated_address(caller_addr.id().unwrap()).map(|a| *a.payload()); + if addresses + .into_iter() + .any(|a| matches!(caller_f4, Some(Payload::Delegated(d)) if d.namespace() == a)) + { + self.caller_validated = true; + Ok(()) + } else { + Err(actor_error!(forbidden; + "caller's namespace {} is not one of supported", caller_addr + )) + } + } + fn validate_immediate_caller_type<'a, I>(&mut self, types: I) -> Result<(), ActorError> where I: IntoIterator, @@ -341,6 +366,11 @@ where .map_err(|_| actor_error!(illegal_argument; "invalid epoch to query tipset_cid")) } + fn emit_event(&self, event: &ActorEvent) -> Result<(), ActorError> { + fvm::event::emit_event(event) + .context_code(ExitCode::USR_ASSERTION_FAILED, "failed to emit event") + } + fn read_only(&self) -> bool { fvm::vm::read_only() } @@ -377,6 +407,11 @@ where .map_err(|e| anyhow!("failed to compute unsealed sector CID; exit code: {}", e)) } + #[cfg(feature = "m2-native")] + fn install_actor(&self, code_id: &Cid) -> Result<(), Error> { + fvm::actor::install_actor(code_id).map_err(|_| Error::msg("failed to install actor")) + } + fn hash(&self, hasher: SupportedHashes, data: &[u8]) -> Vec { fvm::crypto::hash_owned(hasher, data) } @@ -473,7 +508,7 @@ where hasher.update(info.randomness.0); for si in info.challenged_sectors { - hasher.update(RawBytes::serialize(si)?.bytes()); + hasher.update(fvm_ipld_encoding::RawBytes::serialize(si)?.bytes()); } let expected_proof = hasher.finalize(); @@ -543,8 +578,9 @@ pub fn trampoline(params: u32) -> u32 { // Construct a new runtime. let mut rt = FvmRuntime::default(); // Invoke the method, aborting if the actor returns an errored exit code. - let ret = C::invoke_method(&mut rt, method, params) - .unwrap_or_else(|err| fvm::vm::abort(err.exit_code().value(), Some(err.msg()))); + let ret = C::invoke_method(&mut rt, method, params).unwrap_or_else(|mut err| { + fvm::vm::exit(err.exit_code().value(), err.take_data(), Some(err.msg())) + }); // Abort with "assertion failed" if the actor failed to validate the caller somewhere. // We do this after handling the error, because the actor may have encountered an error before diff --git a/runtime/src/runtime/mod.rs b/runtime/src/runtime/mod.rs index ab25f8780..5167c9187 100644 --- a/runtime/src/runtime/mod.rs +++ b/runtime/src/runtime/mod.rs @@ -5,6 +5,7 @@ use cid::Cid; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::CborStore; use fvm_shared::address::Address; +use fvm_shared::chainid::ChainID; use fvm_shared::clock::ChainEpoch; use fvm_shared::consensus::ConsensusFault; use fvm_shared::crypto::hash::SupportedHashes; @@ -12,6 +13,7 @@ use fvm_shared::crypto::signature::{ Signature, SECP_PUB_LEN, SECP_SIG_LEN, SECP_SIG_MESSAGE_HASH_SIZE, }; use fvm_shared::econ::TokenAmount; +use fvm_shared::event::ActorEvent; use fvm_shared::piece::PieceInfo; use fvm_shared::randomness::RANDOMNESS_LENGTH; use fvm_shared::sector::{ @@ -63,12 +65,23 @@ pub trait Runtime: Primitives + Verifier + RuntimePolicy { /// The genesis block has epoch zero. fn curr_epoch(&self) -> ChainEpoch; + /// The ID for the EVM-based chain, as defined in https://github.com/ethereum-lists/chains. + fn chain_id(&self) -> ChainID; + /// Validates the caller against some predicate. /// Exported actor methods must invoke at least one caller validation before returning. fn validate_immediate_caller_accept_any(&mut self) -> Result<(), ActorError>; fn validate_immediate_caller_is<'a, I>(&mut self, addresses: I) -> Result<(), ActorError> where I: IntoIterator; + /// Validates the caller is a member of a namespace. + /// Addresses must be of Protocol ID. + fn validate_immediate_caller_namespace( + &mut self, + namespace_manager_addresses: I, + ) -> Result<(), ActorError> + where + I: IntoIterator; fn validate_immediate_caller_type<'a, I>(&mut self, types: I) -> Result<(), ActorError> where I: IntoIterator; @@ -235,6 +248,9 @@ pub trait Runtime: Primitives + Verifier + RuntimePolicy { /// The epoch must satisfy: (curr_epoch - FINALITY) < epoch <= curr_epoch fn tipset_cid(&self, epoch: i64) -> Result; + /// Emits an event denoting that something externally noteworthy has ocurred. + fn emit_event(&self, event: &ActorEvent) -> Result<(), ActorError>; + /// Returns true if the call is read_only. /// All state updates, including actor creation and balance transfers, are rejected in read_only calls. fn read_only(&self) -> bool; @@ -293,6 +309,9 @@ pub trait Primitives { hash: &[u8; SECP_SIG_MESSAGE_HASH_SIZE], signature: &[u8; SECP_SIG_LEN], ) -> Result<[u8; SECP_PUB_LEN], anyhow::Error>; + + #[cfg(feature = "m2-native")] + fn install_actor(&self, code_cid: &Cid) -> Result<(), anyhow::Error>; } /// filcrypto verification primitives provided by the runtime diff --git a/runtime/src/runtime/policy.rs b/runtime/src/runtime/policy.rs index 06fbf0ab3..57b171bb2 100644 --- a/runtime/src/runtime/policy.rs +++ b/runtime/src/runtime/policy.rs @@ -284,7 +284,8 @@ pub mod policy_constants { /// The period over which all a miner's active sectors will be challenged. pub const WPOST_PROVING_PERIOD: ChainEpoch = EPOCHS_IN_DAY; /// The duration of a deadline's challenge window, the period before a deadline when the challenge is available. - pub const WPOST_CHALLENGE_WINDOW: ChainEpoch = 30 * 60 / EPOCH_DURATION_SECONDS; // Half an hour (=48 per day) + pub const WPOST_CHALLENGE_WINDOW: ChainEpoch = 30 * 60 / EPOCH_DURATION_SECONDS; + // Half an hour (=48 per day) /// The number of non-overlapping PoSt deadlines in each proving period. pub const WPOST_PERIOD_DEADLINES: u64 = 48; /// The maximum distance back that a valid Window PoSt must commit to the current chain. @@ -408,11 +409,14 @@ pub mod policy_constants { pub const MINIMUM_CONSENSUS_POWER: i64 = 2 << 10; #[cfg(feature = "min-power-2g")] pub const MINIMUM_CONSENSUS_POWER: i64 = 2 << 30; + #[cfg(feature = "min-power-16g")] + pub const MINIMUM_CONSENSUS_POWER: i64 = 16 << 30; #[cfg(feature = "min-power-32g")] pub const MINIMUM_CONSENSUS_POWER: i64 = 32 << 30; #[cfg(not(any( feature = "min-power-2k", feature = "min-power-2g", + feature = "min-power-16g", feature = "min-power-32g" )))] pub const MINIMUM_CONSENSUS_POWER: i64 = 10 << 40; diff --git a/runtime/src/runtime/randomness.rs b/runtime/src/runtime/randomness.rs index 840e1dd9c..b303c68e7 100644 --- a/runtime/src/runtime/randomness.rs +++ b/runtime/src/runtime/randomness.rs @@ -17,4 +17,5 @@ pub enum DomainSeparationTag { WindowPoStDeadlineAssignment = 7, MarketDealCronSeed = 8, PoStChainCommit = 9, + EvmPrevRandao = 10, } diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index adf943ee1..2ee7a8fc3 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -12,8 +12,8 @@ use cid::Cid; use fvm_ipld_blockstore::{Blockstore, MemoryBlockstore}; use fvm_ipld_encoding::de::DeserializeOwned; use fvm_ipld_encoding::CborStore; -use fvm_shared::address::Payload; -use fvm_shared::address::{Address, Protocol}; +use fvm_shared::address::{Address, Payload, Protocol}; +use fvm_shared::chainid::ChainID; use fvm_shared::clock::ChainEpoch; use fvm_shared::commcid::{FIL_COMMITMENT_SEALED, FIL_COMMITMENT_UNSEALED}; use fvm_shared::consensus::ConsensusFault; @@ -44,6 +44,7 @@ use crate::runtime::{ Verifier, EMPTY_ARR_CID, }; use crate::{actor_error, ActorError, SendError}; +use fvm_shared::event::ActorEvent; use libsecp256k1::{recover, Message, RecoveryId, Signature as EcsdaSignature}; use fvm_ipld_encoding::ipld_block::IpldBlock; @@ -63,10 +64,9 @@ lazy_static::lazy_static! { pub static ref VERIFREG_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/verifiedregistry"); pub static ref DATACAP_TOKEN_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/datacap"); pub static ref PLACEHOLDER_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/placeholder"); - pub static ref EVM_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/evm"); + pub static ref EAM_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/eam"); pub static ref ETHACCOUNT_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/ethaccount"); - pub static ref ACTOR_TYPES: BTreeMap = { let mut map = BTreeMap::new(); map.insert(*SYSTEM_ACTOR_CODE_ID, Type::System); @@ -82,8 +82,8 @@ lazy_static::lazy_static! { map.insert(*VERIFREG_ACTOR_CODE_ID, Type::VerifiedRegistry); map.insert(*DATACAP_TOKEN_ACTOR_CODE_ID, Type::DataCap); map.insert(*PLACEHOLDER_ACTOR_CODE_ID, Type::Placeholder); - map.insert(*EVM_ACTOR_CODE_ID, Type::EVM); + map.insert(*EAM_ACTOR_CODE_ID, Type::EAM); map.insert(*ETHACCOUNT_ACTOR_CODE_ID, Type::EthAccount); map }; @@ -102,10 +102,13 @@ lazy_static::lazy_static! { (Type::DataCap, *DATACAP_TOKEN_ACTOR_CODE_ID), (Type::Placeholder, *PLACEHOLDER_ACTOR_CODE_ID), (Type::EVM, *EVM_ACTOR_CODE_ID), + (Type::EAM, *EAM_ACTOR_CODE_ID), (Type::EthAccount, *ETHACCOUNT_ACTOR_CODE_ID), ] .into_iter() .collect(); + // TODO this is the other way around and will not work for wasm actors; the singletons must + // be in a map, not the nonsingletons . pub static ref NON_SINGLETON_CODES: BTreeMap = { let mut map = BTreeMap::new(); map.insert(*ACCOUNT_ACTOR_CODE_ID, ()); @@ -126,10 +129,16 @@ pub fn make_identity_cid(bz: &[u8]) -> Cid { Cid::new_v1(IPLD_RAW, OtherMultihash::wrap(0, bz).expect("name too long")) } +/// Enable logging to enviornment. Returns error if already init. +pub fn init_logging() -> Result<(), log::SetLoggerError> { + pretty_env_logger::try_init() +} + pub struct MockRuntime { pub epoch: ChainEpoch, pub miner: Address, pub base_fee: TokenAmount, + pub chain_id: ChainID, pub id_addresses: HashMap, pub delegated_addresses: HashMap, pub actor_code_cids: HashMap, @@ -178,6 +187,7 @@ pub struct MockRuntime { pub struct Expectations { pub expect_validate_caller_any: bool, pub expect_validate_caller_addr: Option>, + pub expect_validate_caller_f4_namespace: Option>, pub expect_validate_caller_type: Option>, pub expect_sends: VecDeque, pub expect_create_actor: Option, @@ -194,6 +204,7 @@ pub struct Expectations { pub expect_replica_verify: Option, pub expect_gas_charge: VecDeque, pub expect_gas_available: VecDeque, + pub expect_emitted_events: VecDeque, skip_verification_on_drop: bool, } @@ -295,6 +306,15 @@ impl Expectations { "expect_gas_available {:?}, not received", this.expect_gas_available ); + assert!( + this.expect_emitted_events.is_empty(), + "expect_emitted_events {:?}, not received", + this.expect_emitted_events + ); + } + + fn skip_verification_on_drop(&mut self) { + self.skip_verification_on_drop = true; } } @@ -310,6 +330,7 @@ impl MockRuntime { epoch: Default::default(), miner: Address::new_id(0), base_fee: Default::default(), + chain_id: ChainID::from(0), id_addresses: Default::default(), delegated_addresses: Default::default(), actor_code_cids: Default::default(), @@ -541,13 +562,14 @@ impl MockRuntime { res } - /// Verifies that all mock expectations have been met (and resets the expectations). + /// Verifies that all mock expectations have been met. pub fn verify(&mut self) { self.expectations.borrow_mut().verify() } /// Clears all mock expectations. pub fn reset(&mut self) { + self.expectations.borrow_mut().skip_verification_on_drop(); self.expectations.borrow_mut().reset(); } @@ -607,6 +629,12 @@ impl MockRuntime { self.expectations.borrow_mut().expect_validate_caller_any = true; } + #[allow(dead_code)] + pub fn expect_validate_caller_namespace(&self, namespaces: Vec) { + assert!(!namespaces.is_empty(), "f4 namespaces must be non-empty"); + self.expectations.borrow_mut().expect_validate_caller_f4_namespace = Some(namespaces); + } + #[allow(dead_code)] pub fn expect_delete_actor(&mut self, beneficiary: Address) { self.expectations.borrow_mut().expect_delete_actor = Some(beneficiary); @@ -765,6 +793,11 @@ impl MockRuntime { self.expectations.borrow_mut().expect_gas_available.push_back(value); } + #[allow(dead_code)] + pub fn expect_emitted_event(&mut self, event: ActorEvent) { + self.expectations.borrow_mut().expect_emitted_events.push_back(event) + } + ///// Private helpers ///// fn require_in_call(&self) { @@ -862,6 +895,53 @@ impl Runtime for MockRuntime { self.message().caller(), &addrs )) } + + fn validate_immediate_caller_namespace(&mut self, namespaces: I) -> Result<(), ActorError> + where + I: IntoIterator, + { + self.require_in_call(); + + let namespaces: Vec = namespaces.into_iter().collect(); + + let mut expectations = self.expectations.borrow_mut(); + assert!( + expectations.expect_validate_caller_f4_namespace.is_some(), + "unexpected validate caller namespace" + ); + + let expected_namespaces = + expectations.expect_validate_caller_f4_namespace.as_ref().unwrap(); + + assert_eq!( + &namespaces, expected_namespaces, + "unexpected validate caller namespace {:?}, expected {:?}", + namespaces, &expectations.expect_validate_caller_f4_namespace + ); + + let caller_f4 = self.lookup_delegated_address(self.caller().id().unwrap()); + + assert!(caller_f4.is_some(), "unexpected caller doesn't have a delegated address"); + + for id in namespaces.iter() { + let bound_address = match caller_f4.unwrap().payload() { + Payload::Delegated(d) => d.namespace(), + _ => unreachable!( + "lookup_delegated_address should always return a delegated address" + ), + }; + if bound_address == *id { + expectations.expect_validate_caller_f4_namespace = None; + return Ok(()); + } + } + expectations.expect_validate_caller_addr = None; + Err(actor_error!(forbidden; + "caller address {:?} forbidden, allowed: {:?}", + self.message().caller(), &namespaces + )) + } + fn validate_immediate_caller_type<'a, I>(&mut self, types: I) -> Result<(), ActorError> where I: IntoIterator, @@ -1176,17 +1256,38 @@ impl Runtime for MockRuntime { } fn tipset_cid(&self, epoch: i64) -> Result { - if epoch > self.epoch || epoch < 0 { + let offset = self.epoch - epoch; + // Can't get tipset for epochs: + // - not current or future epoch + // - not negative + // - before current finality + if offset <= 0 || epoch < 0 || offset > 256 { return Err( actor_error!(illegal_argument; "invalid epoch to fetch tipset_cid {}", epoch), ); } - Ok(*self.tipset_cids.get((self.epoch - epoch) as usize).unwrap()) + Ok(*self.tipset_cids.get(epoch as usize).unwrap()) + } + + fn emit_event(&self, event: &ActorEvent) -> Result<(), ActorError> { + let expected = self + .expectations + .borrow_mut() + .expect_emitted_events + .pop_front() + .expect("unexpected call to emit_evit"); + + assert_eq!(*event, expected); + + Ok(()) + } + + fn chain_id(&self) -> ChainID { + self.chain_id } fn read_only(&self) -> bool { - // Unsupported for unit tests - unimplemented!() + false } } @@ -1273,6 +1374,11 @@ impl Primitives for MockRuntime { Ok(exp.cid) } + #[cfg(feature = "m2-native")] + fn install_actor(&self, _code_cid: &Cid) -> anyhow::Result<(), anyhow::Error> { + Ok(()) + } + fn recover_secp_public_key( &self, hash: &[u8; SECP_SIG_MESSAGE_HASH_SIZE], diff --git a/runtime/src/util/chaos/mod.rs b/runtime/src/util/chaos/mod.rs new file mode 100644 index 000000000..8e66937a7 --- /dev/null +++ b/runtime/src/util/chaos/mod.rs @@ -0,0 +1,216 @@ +// Copyright 2019-2022 ChainSafe Systems +// SPDX-License-Identifier: Apache-2.0, MIT + +use cid::Cid; +use fvm_ipld_encoding::RawBytes; +use fvm_shared::address::Address; +use fvm_shared::error::ExitCode; +use fvm_shared::{MethodNum, METHOD_CONSTRUCTOR}; +use num_derive::FromPrimitive; +use num_traits::FromPrimitive; +pub use state::*; +pub use types::*; + +use crate::runtime::builtins::Type; +use crate::runtime::{ActorCode, Runtime}; +use crate::{actor_error, cbor, ActorError}; + +mod state; +mod types; +mod unmarshallable; + +// * Updated to test-vectors commit: 907892394dd83fe1f4bf1a82146bbbcc58963148 + +// Caller Validation methods +const CALLER_VALIDATION_BRANCH_NONE: i64 = 0; +const CALLER_VALIDATION_BRANCH_TWICE: i64 = 1; +const CALLER_VALIDATION_BRANCH_IS_ADDRESS: i64 = 2; +const CALLER_VALIDATION_BRANCH_IS_TYPE: i64 = 3; + +// Mutate State Branch Methods +const MUTATE_IN_TRANSACTION: i64 = 0; + +/// Chaos actor methods available +#[derive(FromPrimitive)] +#[repr(u64)] +pub enum Method { + Constructor = METHOD_CONSTRUCTOR, + CallerValidation = 2, + CreateActor = 3, + ResolveAddress = 4, + DeleteActor = 5, + Send = 6, + MutateState = 7, + AbortWith = 8, + InspectRuntime = 9, +} + +/// Chaos Actor +pub struct Actor; + +impl Actor { + pub fn send(rt: &mut impl Runtime, arg: SendArgs) -> Result { + rt.validate_immediate_caller_accept_any()?; + + let result = rt.send(&arg.to, arg.method, arg.params, arg.value); + if let Err(e) = result { + Ok(SendReturn { return_value: RawBytes::default(), code: e.exit_code() }) + } else { + Ok(SendReturn { return_value: result.unwrap(), code: ExitCode::OK }) + } + } + + /// Constructor for Account actor + pub fn constructor(_rt: &mut impl Runtime) { + panic!("Constructor should not be called"); + } + + /// CallerValidation violates VM call validation constraints. + /// + /// CALLER_VALIDATION_BRANCH_NONE performs no validation. + /// CALLER_VALIDATION_BRANCH_TWICE validates twice. + /// CALLER_VALIDATION_BRANCH_IS_ADDRESS validates against an empty caller + /// address set. + /// CALLER_VALIDATION_BRANCH_IS_TYPE validates against an empty caller type set. + pub fn caller_validation( + rt: &mut impl Runtime, + args: CallerValidationArgs, + ) -> Result<(), ActorError> { + match args.branch { + x if x == CALLER_VALIDATION_BRANCH_NONE => {} + x if x == CALLER_VALIDATION_BRANCH_TWICE => { + rt.validate_immediate_caller_accept_any()?; + rt.validate_immediate_caller_accept_any()?; + } + x if x == CALLER_VALIDATION_BRANCH_IS_ADDRESS => { + rt.validate_immediate_caller_is(&args.addrs)?; + } + x if x == CALLER_VALIDATION_BRANCH_IS_TYPE => { + let types: Vec = args + .types + .iter() + .map(|typ| rt.resolve_builtin_actor_type(typ).unwrap()) + .collect(); + rt.validate_immediate_caller_type(&types)?; + } + _ => panic!("invalid branch passed to CallerValidation"), + } + Ok(()) + } + + // Creates an actor with the supplied CID and Address. + pub fn create_actor(rt: &mut impl Runtime, arg: CreateActorArgs) -> Result<(), ActorError> { + rt.validate_immediate_caller_accept_any()?; + // TODO Temporarily fine to use default as Undefined Cid, but may need to change in the future + let actor_cid = if arg.undef_cid { Cid::default() } else { arg.cid }; + + let actor_address = arg.actor_id; + + rt.create_actor(actor_cid, actor_address, None) + } + + /// Resolves address, and returns the resolved address (defaulting to 0 ID) and success boolean. + pub fn resolve_address( + rt: &mut impl Runtime, + args: Address, + ) -> Result { + rt.validate_immediate_caller_accept_any()?; + let resolved = rt.resolve_address(&args); + Ok(ResolveAddressResponse { id: resolved.unwrap_or(0), success: resolved.is_some() }) + } + + pub fn delete_actor(rt: &mut impl Runtime, beneficiary: Address) -> Result<(), ActorError> { + rt.validate_immediate_caller_accept_any()?; + rt.delete_actor(&beneficiary) + } + + pub fn mutate_state(rt: &mut impl Runtime, arg: MutateStateArgs) -> Result<(), ActorError> { + rt.validate_immediate_caller_accept_any()?; + + match arg.branch { + x if x == MUTATE_IN_TRANSACTION => rt.transaction(|s: &mut State, _| { + s.value = arg.value; + Ok(()) + }), + + _ => Err(actor_error!(illegal_argument; "Invalid mutate state command given" )), + } + } + + pub fn abort_with(arg: AbortWithArgs) -> Result<(), ActorError> { + if arg.uncontrolled { + panic!("Uncontrolled abort/error"); + } + Err(ActorError::unchecked(arg.code, arg.message)) + } + + pub fn inspect_runtime(rt: &mut impl Runtime) -> Result { + rt.validate_immediate_caller_accept_any()?; + Ok(InspectRuntimeReturn { + caller: rt.message().caller(), + receiver: rt.message().receiver(), + value_received: rt.message().value_received(), + curr_epoch: rt.curr_epoch(), + current_balance: rt.current_balance(), + state: rt.state()?, + }) + } +} + +impl ActorCode for Actor { + fn invoke_method( + rt: &mut RT, + method: MethodNum, + params: &RawBytes, + ) -> Result + where + RT: Runtime, + { + match FromPrimitive::from_u64(method) { + Some(Method::Constructor) => { + Self::constructor(rt); + Ok(RawBytes::default()) + } + Some(Method::CallerValidation) => { + Self::caller_validation(rt, cbor::deserialize_params(params)?)?; + Ok(RawBytes::default()) + } + + Some(Method::CreateActor) => { + Self::create_actor(rt, cbor::deserialize_params(params)?)?; + Ok(RawBytes::default()) + } + Some(Method::ResolveAddress) => { + let res = Self::resolve_address(rt, cbor::deserialize_params(params)?)?; + Ok(RawBytes::serialize(res)?) + } + + Some(Method::Send) => { + let res: SendReturn = Self::send(rt, cbor::deserialize_params(params)?)?; + Ok(RawBytes::serialize(res)?) + } + + Some(Method::DeleteActor) => { + Self::delete_actor(rt, cbor::deserialize_params(params)?)?; + Ok(RawBytes::default()) + } + + Some(Method::MutateState) => { + Self::mutate_state(rt, cbor::deserialize_params(params)?)?; + Ok(RawBytes::default()) + } + + Some(Method::AbortWith) => { + Self::abort_with(cbor::deserialize_params(params)?)?; + Ok(RawBytes::default()) + } + + Some(Method::InspectRuntime) => { + let inspect = Self::inspect_runtime(rt)?; + Ok(RawBytes::serialize(inspect)?) + } + + None => Err(actor_error!(unhandled_message; "Invalid method")), + } + } +} diff --git a/src/lib.rs b/src/lib.rs index bd35e69cb..b635783b3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,6 +17,9 @@ /// - "reward" /// - "system" /// - "verifreg" +/// - "evm" +/// - "eam" +/// - "ethaccount" /// /// The Filecoin client must import the contents of CAR into the blockstore, but /// may opt to exclude the index data structure. diff --git a/state/src/check.rs b/state/src/check.rs index d764fe51d..aab5d4719 100644 --- a/state/src/check.rs +++ b/state/src/check.rs @@ -222,6 +222,7 @@ pub fn check_state_invariants<'a, BS: Blockstore + Debug>( } Some(Type::Placeholder) => {} Some(Type::EVM) => {} + Some(Type::EAM) => {} Some(Type::EthAccount) => {} None => { bail!("unexpected actor code CID {} for address {}", actor.code, key); diff --git a/test_vm/Cargo.toml b/test_vm/Cargo.toml index 5aceb432d..6e37888f0 100644 --- a/test_vm/Cargo.toml +++ b/test_vm/Cargo.toml @@ -26,6 +26,7 @@ fil_actor_verifreg = { version = "10.0.0-alpha.1", path = "../actors/verifreg" } fil_actor_miner = { version = "10.0.0-alpha.1", path = "../actors/miner" } fil_actor_datacap = { version = "10.0.0-alpha.1", path = "../actors/datacap" } fil_actor_evm = { version = "10.0.0-alpha.1", path = "../actors/evm" } +fil_actor_eam = { version = "10.0.0-alpha.1", path = "../actors/eam" } fil_actor_ethaccount = { version = "10.0.0-alpha.1", path = "../actors/ethaccount" } anyhow = "1.0.65" @@ -50,8 +51,16 @@ rand_chacha = "0.3.1" regex = "1" serde = { version = "1.0.136", features = ["derive"] } thiserror = "1.0.30" +libsecp256k1 = { version = "0.7.1"} +fil_actors_evm_shared = { version = "10.0.0-alpha.1", path = "../actors/evm/shared" } [dev-dependencies] cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] } multihash = { version = "0.16.1", default-features = false } test-case = "2.2.1" +ethers = { version = "0.17.0", features = ["abigen"] } +hex = "0.4.3" +hex-literal = "0.3.4" + +[features] +m2-native = [] diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index 96306e908..c3045e4f0 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -6,7 +6,9 @@ use cid::Cid; use fil_actor_account::{Actor as AccountActor, State as AccountState}; use fil_actor_cron::{Actor as CronActor, Entry as CronEntry, State as CronState}; use fil_actor_datacap::{Actor as DataCapActor, State as DataCapState}; +use fil_actor_eam::EamActor; use fil_actor_ethaccount::EthAccountActor; +use fil_actor_evm::EvmContractActor; use fil_actor_init::{Actor as InitActor, ExecReturn, State as InitState}; use fil_actor_market::{Actor as MarketActor, Method as MarketMethod, State as MarketState}; use fil_actor_miner::{Actor as MinerActor, MinerInfo, State as MinerState}; @@ -25,9 +27,9 @@ use fil_actors_runtime::runtime::{ use fil_actors_runtime::test_utils::*; use fil_actors_runtime::{actor_error, SendError}; use fil_actors_runtime::{ - ActorError, BURNT_FUNDS_ACTOR_ADDR, CRON_ACTOR_ADDR, FIRST_NON_SINGLETON_ADDR, INIT_ACTOR_ADDR, - REWARD_ACTOR_ADDR, STORAGE_MARKET_ACTOR_ADDR, STORAGE_POWER_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, - VERIFIED_REGISTRY_ACTOR_ADDR, + ActorError, BURNT_FUNDS_ACTOR_ADDR, CRON_ACTOR_ADDR, EAM_ACTOR_ADDR, FIRST_NON_SINGLETON_ADDR, + INIT_ACTOR_ADDR, REWARD_ACTOR_ADDR, STORAGE_MARKET_ACTOR_ADDR, STORAGE_POWER_ACTOR_ADDR, + SYSTEM_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR, }; use fil_actors_runtime::{MessageAccumulator, DATACAP_TOKEN_ACTOR_ADDR}; use fil_builtin_actors_state::check::check_state_invariants; @@ -40,6 +42,7 @@ use fvm_ipld_hamt::{BytesKey, Hamt, Sha256}; use fvm_shared::address::Address; use fvm_shared::address::Payload; use fvm_shared::bigint::Zero; +use fvm_shared::chainid::ChainID; use fvm_shared::clock::ChainEpoch; use fvm_shared::consensus::ConsensusFault; use fvm_shared::crypto::hash::SupportedHashes; @@ -48,6 +51,7 @@ use fvm_shared::crypto::signature::{ }; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; +use fvm_shared::event::ActorEvent; use fvm_shared::piece::PieceInfo; use fvm_shared::randomness::Randomness; use fvm_shared::randomness::RANDOMNESS_LENGTH; @@ -244,6 +248,12 @@ impl<'bs> VM<'bs> { actor(*VERIFREG_ACTOR_CODE_ID, verifreg_head, 0, TokenAmount::zero(), None), ); + // Ethereum Address Manager + v.set_actor( + EAM_ACTOR_ADDR, + actor(*EAM_ACTOR_CODE_ID, EMPTY_ARR_CID, 0, TokenAmount::zero(), None), + ); + // datacap let datacap_head = v.put_store(&DataCapState::new(&v.store, VERIFIED_REGISTRY_ACTOR_ADDR).unwrap()); @@ -424,6 +434,10 @@ impl<'bs> VM<'bs> { let mut a = self.get_actor(from_id).unwrap(); let call_seq = a.call_seq_num; a.call_seq_num = call_seq + 1; + // EthAccount abstractions turns Placeholders into EthAccounts + if a.code == *PLACEHOLDER_ACTOR_CODE_ID { + a.code = *ETHACCOUNT_ACTOR_CODE_ID; + } self.set_actor(from_id, a); let prior_root = self.checkpoint(); @@ -750,9 +764,8 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { Type::Placeholder => { Err(ActorError::unhandled_message("placeholder actors only handle method 0".into())) } - Type::EVM => { - Err(ActorError::unhandled_message("evm actors are not yet supported".into())) - } + Type::EVM => EvmContractActor::invoke_method(self, self.msg.method, params), + Type::EAM => EamActor::invoke_method(self, self.msg.method, params), Type::EthAccount => EthAccountActor::invoke_method(self, self.msg.method, params), }; if res.is_ok() && !self.caller_validated { @@ -800,7 +813,7 @@ impl<'invocation, 'bs> Runtime for InvocationCtx<'invocation, 'bs> { if self.read_only() { return Err(ActorError::unchecked( ExitCode::USR_READ_ONLY, - "cannot create actor in read-only mode".into(), + "cannot send value in read-only mode".into(), )); } @@ -825,6 +838,10 @@ impl<'invocation, 'bs> Runtime for InvocationCtx<'invocation, 'bs> { self.v.get_epoch() } + fn chain_id(&self) -> ChainID { + ChainID::from(0) + } + fn validate_immediate_caller_accept_any(&mut self) -> Result<(), ActorError> { if self.caller_validated { Err(ActorError::unchecked( @@ -837,6 +854,45 @@ impl<'invocation, 'bs> Runtime for InvocationCtx<'invocation, 'bs> { } } + fn validate_immediate_caller_namespace( + &mut self, + namespace_manager_addresses: I, + ) -> Result<(), ActorError> + where + I: IntoIterator, + { + if self.caller_validated { + return Err(ActorError::unchecked( + ExitCode::SYS_ASSERTION_FAILED, + "caller double validated".to_string(), + )); + } + let managers: Vec<_> = namespace_manager_addresses.into_iter().collect(); + + if let Some(delegated) = + self.lookup_delegated_address(self.message().caller().id().unwrap()) + { + for id in managers { + if match delegated.payload() { + Payload::Delegated(d) => d.namespace() == id, + _ => false, + } { + return Ok(()); + } + } + } else { + return Err(ActorError::unchecked( + ExitCode::SYS_ASSERTION_FAILED, + "immediate caller actor expected to have namespace".to_string(), + )); + } + + Err(ActorError::unchecked( + ExitCode::SYS_ASSERTION_FAILED, + "immediate caller actor namespace forbidden".to_string(), + )) + } + fn validate_immediate_caller_is<'a, I>(&mut self, addresses: I) -> Result<(), ActorError> where I: IntoIterator, @@ -1057,6 +1113,11 @@ impl<'invocation, 'bs> Runtime for InvocationCtx<'invocation, 'bs> { Ok(Cid::new_v1(IPLD_RAW, Multihash::wrap(0, b"faketipset").unwrap())) } + // TODO No support for events yet. + fn emit_event(&self, _event: &ActorEvent) -> Result<(), ActorError> { + Ok(()) + } + fn read_only(&self) -> bool { self.read_only } @@ -1098,6 +1159,11 @@ impl Primitives for VM<'_> { Ok(make_piece_cid(b"unsealed from itest vm")) } + #[cfg(feature = "m2-native")] + fn install_actor(&self, _: &Cid) -> Result<(), anyhow::Error> { + panic!("TODO implement me") + } + fn hash(&self, hasher: SupportedHashes, data: &[u8]) -> Vec { let hasher = Code::try_from(hasher as u64).unwrap(); // supported hashes are all implemented in multihash hasher.digest(data).digest().to_owned() @@ -1140,6 +1206,11 @@ impl Primitives for InvocationCtx<'_, '_> { self.v.compute_unsealed_sector_cid(proof_type, pieces) } + #[cfg(feature = "m2-native")] + fn install_actor(&self, _: &Cid) -> Result<(), anyhow::Error> { + panic!("TODO implement me") + } + fn hash(&self, hasher: SupportedHashes, data: &[u8]) -> Vec { self.v.hash(hasher, data) } diff --git a/test_vm/tests/account_authenticate_message_test.rs b/test_vm/tests/account_authenticate_message_test.rs deleted file mode 100644 index f55cb0c43..000000000 --- a/test_vm/tests/account_authenticate_message_test.rs +++ /dev/null @@ -1,51 +0,0 @@ -use fil_actor_account::types::AuthenticateMessageParams; -use fil_actor_account::Method::AuthenticateMessageExported; -use fvm_ipld_blockstore::MemoryBlockstore; -use fvm_ipld_encoding::RawBytes; -use fvm_shared::bigint::Zero; -use fvm_shared::econ::TokenAmount; -use fvm_shared::error::ExitCode; -use test_vm::util::{apply_code, apply_ok, create_accounts, generate_deal_proposal}; -use test_vm::VM; - -// Using a deal proposal as a serialized message, we confirm that: -// - calls to authenticate_message with valid signatures succeed -// - calls to authenticate_message with invalid signatures fail -#[test] -fn account_authenticate_message() { - let store = MemoryBlockstore::new(); - let v = VM::new_with_singletons(&store); - let addr = create_accounts(&v, 1, TokenAmount::from_whole(10_000))[0]; - - let proposal = - generate_deal_proposal(addr, addr, TokenAmount::zero(), TokenAmount::zero(), 0, 0); - let proposal_ser = - RawBytes::serialize(proposal).expect("failed to marshal deal proposal").to_vec(); - - // With a good sig, message succeeds - let authenticate_message_params = AuthenticateMessageParams { - signature: proposal_ser.clone(), - message: proposal_ser.clone(), - }; - apply_ok( - &v, - addr, - addr, - TokenAmount::zero(), - AuthenticateMessageExported as u64, - Some(authenticate_message_params), - ); - - // Bad, bad sig! message fails - let authenticate_message_params = - AuthenticateMessageParams { signature: vec![], message: proposal_ser }; - apply_code( - &v, - addr, - addr, - TokenAmount::zero(), - AuthenticateMessageExported as u64, - Some(authenticate_message_params), - ExitCode::USR_ILLEGAL_ARGUMENT, - ); -} diff --git a/test_vm/tests/authenticate_message_test.rs b/test_vm/tests/authenticate_message_test.rs new file mode 100644 index 000000000..7717db4a1 --- /dev/null +++ b/test_vm/tests/authenticate_message_test.rs @@ -0,0 +1,171 @@ +use fil_actor_account::types::AuthenticateMessageParams; +use fil_actor_account::Method::AuthenticateMessageExported; +use fil_actors_runtime::test_utils::hash; +use fil_actors_runtime::EAM_ACTOR_ID; +use fvm_ipld_blockstore::MemoryBlockstore; +use fvm_ipld_encoding::RawBytes; +use fvm_shared::address::Address; +use fvm_shared::bigint::Zero; +use fvm_shared::crypto::hash::SupportedHashes::Keccak256; +use fvm_shared::econ::TokenAmount; +use fvm_shared::error::ExitCode; +use fvm_shared::METHOD_SEND; +use rand::SeedableRng; +use rand_chacha::ChaCha8Rng; +use test_vm::util::{apply_code, apply_ok, create_accounts, generate_deal_proposal}; +use test_vm::VM; + +// Using a deal proposal as a serialized message, we confirm that: +// - calls to Account::authenticate_message with valid signatures succeed +// - calls to Account::authenticate_message with invalid signatures fail +#[test] +fn account_authenticate_message() { + let store = MemoryBlockstore::new(); + let v = VM::new_with_singletons(&store); + let addr = create_accounts(&v, 1, TokenAmount::from_whole(10_000))[0]; + + let proposal = + generate_deal_proposal(addr, addr, TokenAmount::zero(), TokenAmount::zero(), 0, 0); + let proposal_ser = + RawBytes::serialize(proposal).expect("failed to marshal deal proposal").to_vec(); + + // With a good sig, message succeeds + let authenticate_message_params = AuthenticateMessageParams { + signature: proposal_ser.clone(), + message: proposal_ser.clone(), + }; + apply_ok( + &v, + addr, + addr, + TokenAmount::zero(), + AuthenticateMessageExported as u64, + Some(authenticate_message_params), + ); + + // Bad, bad sig! message fails + let authenticate_message_params = + AuthenticateMessageParams { signature: vec![], message: proposal_ser }; + apply_code( + &v, + addr, + addr, + TokenAmount::zero(), + AuthenticateMessageExported as u64, + Some(authenticate_message_params), + ExitCode::USR_ILLEGAL_ARGUMENT, + ); +} + +// Using a deal proposal as a serialized message, we confirm that: +// - calls to EthAccount::authenticate_message with valid signatures succeed +#[test] +fn ethaccount_authenticate_message_success() { + let store = MemoryBlockstore::new(); + let v = VM::new_with_singletons(&store); + let addr = create_accounts(&v, 1, TokenAmount::from_whole(10_000))[0]; + let rng = &mut ChaCha8Rng::seed_from_u64(0); + let secret_key = libsecp256k1::SecretKey::random(rng); + + let proposal = + generate_deal_proposal(addr, addr, TokenAmount::zero(), TokenAmount::zero(), 0, 0); + let proposal_ser = + RawBytes::serialize(proposal).expect("failed to marshal deal proposal").to_vec(); + + let msg_hash = get_hash_for_signature(&proposal_ser); + let (good_sig, recovery_id) = libsecp256k1::sign(&msg_hash, &secret_key); + + let pub_key = libsecp256k1::recover(&msg_hash, &good_sig, &recovery_id).unwrap(); + let pub_key_ser = pub_key.serialize(); + let pub_key_hash = hash(Keccak256, &pub_key_ser[1..]).0; + + let eth_addr = Address::new_delegated(EAM_ACTOR_ID, &pub_key_hash[12..32]).unwrap(); + + // Create a Placeholder by sending to it + apply_ok(&v, addr, eth_addr, TokenAmount::from_whole(2), METHOD_SEND, None::); + + // Create the EthAccount by sending from the Placeholder + apply_ok(&v, eth_addr, addr, TokenAmount::from_whole(1), METHOD_SEND, None::); + + let mut good_sig_ser = [0; 65]; + good_sig_ser[..64].copy_from_slice(&good_sig.serialize()); + good_sig_ser[64] = recovery_id.serialize(); + + // With a good sig, message succeeds + let authenticate_message_params = + AuthenticateMessageParams { signature: good_sig_ser.to_vec(), message: proposal_ser }; + apply_ok( + &v, + addr, + eth_addr, + TokenAmount::zero(), + AuthenticateMessageExported as u64, + Some(authenticate_message_params), + ); +} + +// Using a deal proposal as a serialized message, we confirm that +// calls to EthAccount::authenticate_message with invalid signatures fail +#[test] +fn ethaccount_authenticate_message_failure() { + let store = MemoryBlockstore::new(); + let v = VM::new_with_singletons(&store); + let addr = create_accounts(&v, 1, TokenAmount::from_whole(10_000))[0]; + let rng = &mut ChaCha8Rng::seed_from_u64(0); + let secret_key = libsecp256k1::SecretKey::random(rng); + + let proposal = + generate_deal_proposal(addr, addr, TokenAmount::zero(), TokenAmount::zero(), 0, 0); + let proposal_ser = + RawBytes::serialize(proposal).expect("failed to marshal deal proposal").to_vec(); + + let msg_hash = get_hash_for_signature(&proposal_ser); + let (good_sig, recovery_id) = libsecp256k1::sign(&msg_hash, &secret_key); + + let pub_key = libsecp256k1::recover(&msg_hash, &good_sig, &recovery_id).unwrap(); + let pub_key_ser = pub_key.serialize(); + let pub_key_hash = hash(Keccak256, &pub_key_ser[1..]).0; + + let eth_addr = Address::new_delegated(EAM_ACTOR_ID, &pub_key_hash[12..32]).unwrap(); + + // Create a Placeholder by sending to it + apply_ok(&v, addr, eth_addr, TokenAmount::from_whole(2), METHOD_SEND, None::); + + // Create the EthAccount by sending from the Placeholder + apply_ok(&v, eth_addr, addr, TokenAmount::from_whole(1), METHOD_SEND, None::); + + // To test a bad sig, we sign the correct payload with a different key (this is a bit more comprehensive than simply flipping a bit) + + let other_key = libsecp256k1::SecretKey::random(rng); + assert_ne!(secret_key, other_key); + + let (bad_sig, bad_recovery_id) = libsecp256k1::sign(&msg_hash, &other_key); + let mut bad_sig_ser = [0; 65]; + bad_sig_ser[..64].copy_from_slice(&bad_sig.serialize()); + bad_sig_ser[64] = bad_recovery_id.serialize(); + + let authenticate_message_params = + AuthenticateMessageParams { signature: bad_sig_ser.to_vec(), message: proposal_ser }; + apply_code( + &v, + addr, + addr, + TokenAmount::zero(), + AuthenticateMessageExported as u64, + Some(authenticate_message_params), + ExitCode::USR_ILLEGAL_ARGUMENT, + ); +} + +fn get_hash_for_signature(bytes: &[u8]) -> libsecp256k1::Message { + let hash: [u8; 32] = blake2b_simd::Params::new() + .hash_length(32) + .to_state() + .update(bytes) + .finalize() + .as_bytes() + .try_into() + .expect("fixed array size"); + + libsecp256k1::Message::parse(&hash) +} diff --git a/test_vm/tests/evm_test.rs b/test_vm/tests/evm_test.rs new file mode 100644 index 000000000..df754f133 --- /dev/null +++ b/test_vm/tests/evm_test.rs @@ -0,0 +1,740 @@ +use std::sync::Arc; + +use ethers::prelude::abigen; +use ethers::providers::Provider; +use ethers::{core::types::Address as EthAddress, prelude::builders::ContractCall}; +use fil_actors_evm_shared::uints::U256; +use fil_actors_runtime::{ + test_utils::{ETHACCOUNT_ACTOR_CODE_ID, EVM_ACTOR_CODE_ID}, + EAM_ACTOR_ADDR, EAM_ACTOR_ID, +}; +use fvm_ipld_blockstore::MemoryBlockstore; +use fvm_ipld_encoding::{strict_bytes, BytesDe, RawBytes}; +use fvm_shared::{address::Address, econ::TokenAmount}; +use fvm_shared::{ActorID, METHOD_SEND}; +use num_traits::Zero; +use serde::{Deserialize, Serialize}; +use test_vm::{ + util::{apply_ok, create_accounts}, + TEST_FAUCET_ADDR, VM, +}; + +// Generate a statically typed interface for the contracts. +abigen!(Recursive, "../actors/evm/tests/contracts/Recursive.abi"); +abigen!(Factory, "../actors/evm/tests/contracts/Factory.abi"); +abigen!(FactoryChild, "../actors/evm/tests/contracts/FactoryChild.abi"); + +fn id_to_eth(id: ActorID) -> EthAddress { + let mut addr = [0u8; 20]; + addr[0] = 0xff; + addr[12..].copy_from_slice(&id.to_be_bytes()); + EthAddress::from_slice(&addr) +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(transparent)] +struct ContractParams(#[serde(with = "strict_bytes")] pub Vec); + +#[test] +fn test_evm_call() { + let store = MemoryBlockstore::new(); + let v = VM::new_with_singletons(&store); + + let account = create_accounts(&v, 1, TokenAmount::from_whole(10_000))[0]; + + let address = id_to_eth(account.id().unwrap()); + let (client, _mock) = Provider::mocked(); + let contract = Recursive::new(address, Arc::new(client)); + + let bytecode = + hex::decode(include_str!("../../actors/evm/tests/contracts/Recursive.hex")).unwrap(); + + let create_result = v + .apply_message( + account, + EAM_ACTOR_ADDR, + TokenAmount::zero(), + fil_actor_eam::Method::CreateExternal as u64, + Some(fil_actor_eam::CreateExternalParams(bytecode)), + ) + .unwrap(); + + assert!( + create_result.code.is_success(), + "failed to create the new actor {}", + create_result.message + ); + + let create_return: fil_actor_eam::CreateExternalReturn = + create_result.ret.unwrap().deserialize().expect("failed to decode results"); + + let contract_params = contract.enter().calldata().expect("should serialize"); + let call_result = v + .apply_message( + account, + create_return.robust_address.unwrap(), + TokenAmount::zero(), + fil_actor_evm::Method::InvokeContract as u64, + Some(ContractParams(contract_params.to_vec())), + ) + .unwrap(); + assert!(call_result.code.is_success(), "failed to call the new actor {}", call_result.message); + let BytesDe(return_value) = + call_result.ret.unwrap().deserialize().expect("failed to deserialize results"); + let evm_ret: u32 = contract + .decode_output(&contract.enter().function.name, &return_value) + .expect("failed to decode return"); + assert_eq!(0, evm_ret, "expected contract to return 0 on success"); +} + +#[test] +fn test_evm_create() { + let store = MemoryBlockstore::new(); + let v = VM::new_with_singletons(&store); + + let account = create_accounts(&v, 1, TokenAmount::from_whole(10_000))[0]; + + let address = id_to_eth(account.id().unwrap()); + let (client, _mock) = Provider::mocked(); + let client = Arc::new(client); + let factory = Factory::new(address, client.clone()); + let factory_child = FactoryChild::new(address, client); + + let bytecode = + hex::decode(include_str!("../../actors/evm/tests/contracts/Lifecycle.hex")).unwrap(); + + let create_result = v + .apply_message( + account, + EAM_ACTOR_ADDR, + TokenAmount::zero(), + fil_actor_eam::Method::CreateExternal as u64, + Some(fil_actor_eam::CreateExternalParams(bytecode)), + ) + .unwrap(); + + assert!( + create_result.code.is_success(), + "failed to create the new actor {}", + create_result.message + ); + + let create_return: fil_actor_eam::CreateExternalReturn = + create_result.ret.unwrap().deserialize().expect("failed to decode results"); + + let test_func = |create_func: ContractCall<_, EthAddress>| { + let child_addr_eth: EthAddress = { + let call_params = create_func.calldata().expect("should serialize"); + let call_result = v + .apply_message( + account, + create_return.robust_address.unwrap(), + TokenAmount::zero(), + fil_actor_evm::Method::InvokeContract as u64, + Some(ContractParams(call_params.to_vec())), + ) + .unwrap(); + assert!( + call_result.code.is_success(), + "failed to call the new actor {}", + call_result.message, + ); + let BytesDe(return_value) = + call_result.ret.unwrap().deserialize().expect("failed to deserialize results"); + factory + .decode_output(&create_func.function.name, &return_value) + .expect("failed to decode return") + }; + + let child_addr = Address::new_delegated(EAM_ACTOR_ID, &child_addr_eth.0[..]).unwrap(); + + // Verify the child. + { + let func = factory_child.get_value(); + let call_params = func.calldata().expect("should serialize"); + let call_result = v + .apply_message( + account, + child_addr, + TokenAmount::zero(), + fil_actor_evm::Method::InvokeContract as u64, + Some(ContractParams(call_params.to_vec())), + ) + .unwrap(); + assert!( + call_result.code.is_success(), + "failed to call the new actor {}", + call_result.message + ); + let BytesDe(return_value) = + call_result.ret.unwrap().deserialize().expect("failed to deserialize results"); + let res: u32 = factory_child + .decode_output(&func.function.name, &return_value) + .expect("failed to decode return"); + assert_eq!(res, 42); + } + + // Kill it. + { + let func = factory_child.die(); + let call_params = func.calldata().expect("should serialize"); + let call_result = v + .apply_message( + account, + child_addr, + TokenAmount::zero(), + fil_actor_evm::Method::InvokeContract as u64, + Some(ContractParams(call_params.to_vec())), + ) + .unwrap(); + assert!( + call_result.code.is_success(), + "failed to call the new actor {}", + call_result.message + ); + } + + // It should now be dead. + { + let func = factory_child.get_value(); + let call_params = func.calldata().expect("should serialize"); + let call_result = v + .apply_message( + account, + child_addr, + TokenAmount::zero(), + fil_actor_evm::Method::InvokeContract as u64, + Some(ContractParams(call_params.to_vec())), + ) + .unwrap(); + assert!( + call_result.code.is_success(), + "failed to call the new actor {}", + call_result.message + ); + let BytesDe(return_value) = + call_result.ret.unwrap().deserialize().expect("failed to deserialize results"); + assert!(return_value.is_empty()); + } + child_addr_eth + }; + + // Test CREATE2 twice because we should be able to deploy over an existing contract. + let eth_addr1 = test_func(factory.create_2([0; 32], 42)); + let eth_addr2 = test_func(factory.create_2([0; 32], 42)); + assert_eq!(eth_addr1, eth_addr2); + + // Then test create and expect two different addrs. + let eth_addr1 = test_func(factory.create(42)); + let eth_addr2 = test_func(factory.create(42)); + assert_ne!(eth_addr1, eth_addr2); +} + +#[test] +fn test_evm_eth_create_external() { + let store = MemoryBlockstore::new(); + let v = VM::new_with_singletons(&store); + + // create the EthAccount + let eth_bits = hex_literal::hex!("FEEDFACECAFEBEEF000000000000000000000000"); + let eth_addr = Address::new_delegated(EAM_ACTOR_ID, ð_bits).unwrap(); + apply_ok( + &v, + TEST_FAUCET_ADDR, + eth_addr, + TokenAmount::from_whole(10_000), + METHOD_SEND, + None::, + ); + let account = v.normalize_address(ð_addr).unwrap(); + let mut actor = v.get_actor(account).unwrap(); + actor.code = *ETHACCOUNT_ACTOR_CODE_ID; + v.set_actor(account, actor); + + // now create an empty contract + let create_result = v + .apply_message( + account, + EAM_ACTOR_ADDR, + TokenAmount::zero(), + fil_actor_eam::Method::CreateExternal as u64, + Some(fil_actor_eam::CreateExternalParams(vec![])), + ) + .unwrap(); + + assert!( + create_result.code.is_success(), + "failed to create the new actor {}", + create_result.message + ); + + // and call it + let create_return: fil_actor_eam::CreateExternalReturn = + create_result.ret.unwrap().deserialize().expect("failed to decode results"); + + let robust_addr = create_return.robust_address.unwrap(); + + let call_result = v + .apply_message( + account, + robust_addr, + TokenAmount::zero(), + fil_actor_evm::Method::InvokeContract as u64, + Some(ContractParams(vec![])), + ) + .unwrap(); + assert!(call_result.code.is_success(), "failed to call the new actor {}", call_result.message); +} + +#[test] +fn test_evm_empty_initcode() { + let store = MemoryBlockstore::new(); + let v = VM::new_with_singletons(&store); + + let account = create_accounts(&v, 1, TokenAmount::from_whole(10_000))[0]; + let create_result = v + .apply_message( + account, + EAM_ACTOR_ADDR, + TokenAmount::zero(), + fil_actor_eam::Method::CreateExternal as u64, + Some(fil_actor_eam::CreateExternalParams(vec![])), + ) + .unwrap(); + + assert!( + create_result.code.is_success(), + "failed to create the new actor {}", + create_result.message + ); +} + +#[test] +#[allow(non_snake_case)] +fn test_evm_staticcall() { + // test scenarios: + // one hop: + // A -> staticcall -> B (read) OK + // A -> staticcall -> B (write) FAIL + // two hop sticky: + // A -> staticcall -> B -> call -> C (read) OK + // A -> staticcall -> B -> call -> C (write) FAIL + + let store = MemoryBlockstore::new(); + let v = VM::new_with_singletons(&store); + + let accounts = create_accounts(&v, 3, TokenAmount::from_whole(10_000)); + + let bytecode = + hex::decode(include_str!("../../actors/evm/tests/contracts/callvariants.hex")).unwrap(); + + let created: Vec<_> = accounts + .iter() + .map(|account| { + let create_result = v + .apply_message( + *account, + EAM_ACTOR_ADDR, + TokenAmount::zero(), + fil_actor_eam::Method::CreateExternal as u64, + Some(fil_actor_eam::CreateExternalParams(bytecode.clone())), + ) + .unwrap(); + + assert!( + create_result.code.is_success(), + "failed to create the new actor {}", + create_result.message + ); + + let create_return: fil_actor_eam::CreateExternalReturn = + create_result.ret.unwrap().deserialize().expect("failed to decode results"); + + // Make sure we deployed an EVM actor. + assert_eq!( + &v.get_actor(Address::new_id(create_return.actor_id)).unwrap().code, + &*EVM_ACTOR_CODE_ID + ); + + create_return + }) + .collect(); + + // A -> staticcall -> B (read) OK + { + let A_act = accounts[0]; + let A_robust_addr = created[0].robust_address.unwrap(); + let B = id_to_eth(created[1].actor_id); + let mut params = [0u8; 36]; + params[3] = 1; + params[16..].copy_from_slice(B.as_ref()); + + let call_result = v + .apply_message( + A_act, + A_robust_addr, + TokenAmount::zero(), + fil_actor_evm::Method::InvokeContract as u64, + Some(ContractParams(params.to_vec())), + ) + .unwrap(); + assert!( + call_result.code.is_success(), + "failed to call the new actor {}", + call_result.message + ); + let BytesDe(return_value) = + call_result.ret.unwrap().deserialize().expect("failed to deserialize results"); + assert_eq!(&return_value[12..], &created[1].eth_address.0); + } + + // A -> staticcall -> B (write) FAIL + { + let A_act = accounts[0]; + let A_robust_addr = created[0].robust_address.unwrap(); + let B = id_to_eth(created[1].actor_id); + let mut params = [0u8; 36]; + params[3] = 3; + params[16..].copy_from_slice(B.as_ref()); + + let call_result = v + .apply_message( + A_act, + A_robust_addr, + TokenAmount::zero(), + fil_actor_evm::Method::InvokeContract as u64, + Some(ContractParams(params.to_vec())), + ) + .unwrap(); + assert_eq!(call_result.code.value(), 33, "static call mutation did not revert"); + } + + // A -> staticcall -> B -> call -> C (read) OK + { + let A_act = accounts[0]; + let A_robust_addr = created[0].robust_address.unwrap(); + let B = id_to_eth(created[1].actor_id); + let C = id_to_eth(created[2].actor_id); + let mut params = [0u8; 68]; + params[3] = 5; + params[16..][..20].copy_from_slice(B.as_ref()); + params[48..].copy_from_slice(C.as_ref()); + + let call_result = v + .apply_message( + A_act, + A_robust_addr, + TokenAmount::zero(), + fil_actor_evm::Method::InvokeContract as u64, + Some(ContractParams(params.to_vec())), + ) + .unwrap(); + assert!( + call_result.code.is_success(), + "failed to call the new actor {}", + call_result.message + ); + let BytesDe(return_value) = + call_result.ret.unwrap().deserialize().expect("failed to deserialize results"); + assert_eq!(&return_value[12..], &created[2].eth_address.0); + } + + // A -> staticcall -> B -> call -> C (write) FAIL + { + let A_act = accounts[0]; + let A_robust_addr = created[0].robust_address.unwrap(); + let B = id_to_eth(created[1].actor_id); + let C = id_to_eth(created[2].actor_id); + let mut params = [0u8; 68]; + params[3] = 7; + params[16..][..20].copy_from_slice(B.as_ref()); + params[48..].copy_from_slice(C.as_ref()); + + let call_result = v + .apply_message( + A_act, + A_robust_addr, + TokenAmount::zero(), + fil_actor_evm::Method::InvokeContract as u64, + Some(ContractParams(params.to_vec())), + ) + .unwrap(); + assert_eq!(call_result.code.value(), 33, "static call mutation did not revert"); + } +} + +#[test] +#[allow(non_snake_case)] +fn test_evm_delegatecall() { + // test scenarios: + // one hop: + // A -> delegatecall -> B (read) OK + // A -> delegatecall -> B (write) -> return (read) OK + // two hop with sticky staticcall: + // A -> staticcall -> B -> delegatecall -> C (read) OK + // A -> staticcall -> B -> delegatecall -> C (write) FAIL + + let store = MemoryBlockstore::new(); + let v = VM::new_with_singletons(&store); + + let accounts = create_accounts(&v, 3, TokenAmount::from_whole(10_000)); + + let bytecode = + hex::decode(include_str!("../../actors/evm/tests/contracts/callvariants.hex")).unwrap(); + + let created: Vec<_> = accounts + .iter() + .map(|account| { + let create_result = v + .apply_message( + *account, + EAM_ACTOR_ADDR, + TokenAmount::zero(), + fil_actor_eam::Method::CreateExternal as u64, + Some(fil_actor_eam::CreateExternalParams(bytecode.clone())), + ) + .unwrap(); + + assert!( + create_result.code.is_success(), + "failed to create the new actor {}", + create_result.message + ); + + let create_return: fil_actor_eam::CreateExternalReturn = + create_result.ret.unwrap().deserialize().expect("failed to decode results"); + + // Make sure we deployed an EVM actor. + assert_eq!( + &v.get_actor(Address::new_id(create_return.actor_id)).unwrap().code, + &*EVM_ACTOR_CODE_ID + ); + + create_return + }) + .collect(); + + // A -> delegatecall -> B (read) OK + { + let A_act = accounts[0]; + let A_robust_addr = created[0].robust_address.unwrap(); + let B = id_to_eth(created[1].actor_id); + let mut params = [0u8; 36]; + params[3] = 9; + params[16..].copy_from_slice(B.as_ref()); + + let call_result = v + .apply_message( + A_act, + A_robust_addr, + TokenAmount::zero(), + fil_actor_evm::Method::InvokeContract as u64, + Some(ContractParams(params.to_vec())), + ) + .unwrap(); + assert!( + call_result.code.is_success(), + "failed to call the new actor {}", + call_result.message + ); + let BytesDe(return_value) = + call_result.ret.unwrap().deserialize().expect("failed to deserialize results"); + assert_eq!(&return_value[12..], &created[0].eth_address.0); + } + + // A -> delegatecall -> B (write) -> return (read) OK + { + let A_act = accounts[0]; + let A_robust_addr = created[0].robust_address.unwrap(); + let B = id_to_eth(created[1].actor_id); + let mut params = [0u8; 36]; + params[3] = 10; + params[16..].copy_from_slice(B.as_ref()); + + let call_result = v + .apply_message( + A_act, + A_robust_addr, + TokenAmount::zero(), + fil_actor_evm::Method::InvokeContract as u64, + Some(ContractParams(params.to_vec())), + ) + .unwrap(); + assert!( + call_result.code.is_success(), + "failed to call the new actor {}", + call_result.message + ); + let BytesDe(return_value) = + call_result.ret.unwrap().deserialize().expect("failed to deserialize results"); + assert_eq!(&return_value[28..], &[0xff, 0xff, 0xff, 0x42]); + } + + // A -> delegatecall -> B (return value received) OK + { + let A_act = accounts[0]; + let A_robust_addr = created[0].robust_address.unwrap(); + let B = id_to_eth(created[1].actor_id); + let mut params = [0u8; 36]; + params[3] = 16; + params[16..].copy_from_slice(B.as_ref()); + + let value = TokenAmount::from_whole(123); + + let call_result = v + .apply_message( + A_act, + A_robust_addr, + value.clone(), + fil_actor_evm::Method::InvokeContract as u64, + Some(ContractParams(params.to_vec())), + ) + .unwrap(); + assert!( + call_result.code.is_success(), + "failed to call the new actor {}", + call_result.message + ); + let BytesDe(return_value) = + call_result.ret.unwrap().deserialize().expect("failed to deserialize results"); + assert_eq!(&return_value, &U256::from(&value).to_bytes()[..]); + } +} + +#[test] +#[allow(non_snake_case)] +fn test_evm_staticcall_delegatecall() { + // test scenarios: + // one hop: + // A -> delegatecall -> B (read) OK + // A -> delegatecall -> B (write) -> return (read) OK + // two hop with sticky staticcall: + // A -> staticcall -> B -> delegatecall -> C (read) OK + // A -> staticcall -> B -> delegatecall -> C (write) FAIL + + let store = MemoryBlockstore::new(); + let v = VM::new_with_singletons(&store); + + let accounts = create_accounts(&v, 3, TokenAmount::from_whole(10_000)); + + let bytecode = + hex::decode(include_str!("../../actors/evm/tests/contracts/callvariants.hex")).unwrap(); + + let created: Vec<_> = accounts + .iter() + .map(|account| { + let create_result = v + .apply_message( + *account, + EAM_ACTOR_ADDR, + TokenAmount::zero(), + fil_actor_eam::Method::CreateExternal as u64, + Some(fil_actor_eam::CreateExternalParams(bytecode.clone())), + ) + .unwrap(); + + assert!( + create_result.code.is_success(), + "failed to create the new actor {}", + create_result.message + ); + + let create_return: fil_actor_eam::CreateExternalReturn = + create_result.ret.unwrap().deserialize().expect("failed to decode results"); + + // Make sure we deployed an EVM actor. + assert_eq!( + &v.get_actor(Address::new_id(create_return.actor_id)).unwrap().code, + &*EVM_ACTOR_CODE_ID + ); + + create_return + }) + .collect(); + + // A -> staticcall -> B -> delegatecall -> C (read) OK + { + let A_act = accounts[0]; + let A_robust_addr = created[0].robust_address.unwrap(); + let B = id_to_eth(created[1].actor_id); + let C = id_to_eth(created[2].actor_id); + let mut params = [0u8; 68]; + params[3] = 11; + params[16..][..20].copy_from_slice(B.as_ref()); + params[48..].copy_from_slice(C.as_ref()); + + let call_result = v + .apply_message( + A_act, + A_robust_addr, + TokenAmount::zero(), + fil_actor_evm::Method::InvokeContract as u64, + Some(ContractParams(params.to_vec())), + ) + .unwrap(); + assert!( + call_result.code.is_success(), + "failed to call the new actor {}", + call_result.message + ); + let BytesDe(return_value) = + call_result.ret.unwrap().deserialize().expect("failed to deserialize results"); + //assert_eq!(&return_value[12..], &created[1].eth_address.0); + println!("return {:?}", return_value) + } + + // A -> staticcall -> B -> delegatecall -> C (write) FAIL + { + let A_act = accounts[0]; + let A_robust_addr = created[0].robust_address.unwrap(); + let B = id_to_eth(created[1].actor_id); + let C = id_to_eth(created[2].actor_id); + let mut params = [0u8; 68]; + params[3] = 13; + params[16..][..20].copy_from_slice(B.as_ref()); + params[48..].copy_from_slice(C.as_ref()); + + let call_result = v + .apply_message( + A_act, + A_robust_addr, + TokenAmount::zero(), + fil_actor_evm::Method::InvokeContract as u64, + Some(ContractParams(params.to_vec())), + ) + .unwrap(); + assert_eq!(call_result.code.value(), 33, "static call mutation did not revert"); + } +} + +#[test] +fn test_evm_init_revert_data() { + let store = MemoryBlockstore::new(); + let v = VM::new_with_singletons(&store); + + let account = create_accounts(&v, 1, TokenAmount::from_whole(10_000))[0]; + let create_result = v + .apply_message( + account, + EAM_ACTOR_ADDR, + TokenAmount::zero(), + fil_actor_eam::Method::CreateExternal as u64, + // init code: + // PUSH1 0x42; PUSH1 0x0; MSTORE; + // PUSH1 0x20; PUSH1 0x0; REVERT + Some(fil_actor_eam::CreateExternalParams(vec![ + 0x60, 0x42, 0x60, 0x00, 0x52, 0x60, 0x20, 0x60, 0x00, 0xfd, + ])), + ) + .unwrap(); + + assert!(!create_result.code.is_success(), "new actor was successfully created!"); + + assert!(create_result.ret.is_some(), "missing return data!"); + + let BytesDe(revert_data) = + create_result.ret.unwrap().deserialize().expect("failed to deserialize revert data"); + let mut expected = [0u8; 32]; + expected[31] = 0x42; + assert_eq!(revert_data, expected); +} diff --git a/test_vm/tests/init_test.rs b/test_vm/tests/init_test.rs new file mode 100644 index 000000000..cc9437c51 --- /dev/null +++ b/test_vm/tests/init_test.rs @@ -0,0 +1,95 @@ +use fil_actor_init::Exec4Return; +use fil_actors_runtime::{ + cbor::serialize, + runtime::EMPTY_ARR_CID, + test_utils::{EAM_ACTOR_CODE_ID, MULTISIG_ACTOR_CODE_ID, PLACEHOLDER_ACTOR_CODE_ID}, + EAM_ACTOR_ADDR, EAM_ACTOR_ID, INIT_ACTOR_ADDR, +}; +use fvm_ipld_blockstore::MemoryBlockstore; +use fvm_ipld_encoding::RawBytes; +use fvm_shared::{address::Address, econ::TokenAmount, error::ExitCode, METHOD_SEND}; +use num_traits::Zero; +use test_vm::{actor, FIRST_TEST_USER_ADDR, TEST_FAUCET_ADDR, VM}; + +fn assert_placeholder_actor(exp_bal: TokenAmount, v: &VM, addr: Address) { + let act = v.get_actor(addr).unwrap(); + assert_eq!(EMPTY_ARR_CID, act.head); + assert_eq!(*PLACEHOLDER_ACTOR_CODE_ID, act.code); + assert_eq!(exp_bal, act.balance); +} + +#[test] +fn placeholder_deploy() { + let store = MemoryBlockstore::new(); + let v = VM::new_with_singletons(&store); + + // Create a "fake" eam. + v.set_actor( + EAM_ACTOR_ADDR, + actor(*EAM_ACTOR_CODE_ID, EMPTY_ARR_CID, 0, TokenAmount::zero(), None), + ); + + // Create a placeholder. + + let subaddr = b"foobar"; + let addr = Address::new_delegated(EAM_ACTOR_ID, subaddr).unwrap(); + assert!(v + .apply_message( + TEST_FAUCET_ADDR, + addr, + TokenAmount::from_atto(42u8), + METHOD_SEND, + None::, + ) + .unwrap() + .code + .is_success()); + let expect_id_addr = Address::new_id(FIRST_TEST_USER_ADDR); + assert_placeholder_actor(TokenAmount::from_atto(42u8), &v, expect_id_addr); + + // Make sure we assigned the right f4 address. + assert_eq!(v.normalize_address(&addr).unwrap(), expect_id_addr); + + // Deploy a multisig to the placeholder. + let msig_ctor_params = serialize( + &fil_actor_multisig::ConstructorParams { + signers: vec![EAM_ACTOR_ADDR], + num_approvals_threshold: 1, + unlock_duration: 0, + start_epoch: 0, + }, + "multisig ctor params", + ) + .unwrap(); + + let deploy = || { + v.apply_message( + EAM_ACTOR_ADDR, // so this works even if "m2-native" is disabled. + INIT_ACTOR_ADDR, + TokenAmount::zero(), + fil_actor_init::Method::Exec4 as u64, + Some(fil_actor_init::Exec4Params { + code_cid: *MULTISIG_ACTOR_CODE_ID, + constructor_params: msig_ctor_params.clone(), + subaddress: subaddr[..].to_owned().into(), + }), + ) + .unwrap() + }; + + let msig_ctor_res = deploy(); + assert_eq!(msig_ctor_res.code, ExitCode::OK); + let msig_ctor_ret: Exec4Return = msig_ctor_res.ret.unwrap().deserialize().unwrap(); + + assert_eq!( + expect_id_addr, msig_ctor_ret.id_address, + "expected actor to be deployed over placeholder" + ); + + // Make sure we kept the balance. + assert_eq!(v.get_actor(expect_id_addr).unwrap().balance, TokenAmount::from_atto(42u8)); + + // Try to overwrite it. + let msig_ctor_res = deploy(); + assert_eq!(ExitCode::USR_FORBIDDEN, msig_ctor_res.code); +} diff --git a/test_vm/tests/test_vm_test.rs b/test_vm/tests/test_vm_test.rs index 06e465436..e3d3c6256 100644 --- a/test_vm/tests/test_vm_test.rs +++ b/test_vm/tests/test_vm_test.rs @@ -39,6 +39,7 @@ fn state_control() { TokenAmount::from_atto(1u8), None, ); + v.set_actor(addr2, a2.clone()); assert_eq!(v.get_actor(addr2).unwrap(), a2); // rollback removes a2 but not a1